From f55c124be67be8e51dc7ed44f3165d8ffa696f1e Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Thu, 20 Oct 2005 13:02:26 +0000 Subject: [PATCH] fundamental changes in plugin-architecture, added plugin-browser, bug-fixes - see ChangeLog for further details git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@18 0778d3d1-df1d-0410-868b-ea421aaaa00d --- ChangeLog | 141 ++ Makefile.am | 60 +- README | 29 +- TODO | 18 +- buildtools/Makefile.am | 5 + buildtools/bin2res.cpp | 19 +- configure.in | 15 +- include/audio_alsa.h | 2 +- include/audio_device.h | 6 +- include/audio_file_ogg.h | 2 +- include/audio_file_wave.h | 2 +- include/audio_jack.h | 2 +- include/audio_oss.h | 2 +- include/audio_sample_recorder.h | 2 +- include/audio_sdl.h | 2 +- include/basic_filters.h | 12 +- include/bb_editor.h | 8 +- include/config_mgr.h | 6 + include/dummy_instrument.h | 61 + include/dummy_plugin.h | 61 + include/embed.h | 21 + include/ladspa_manager.h | 15 +- include/mixer.h | 5 +- include/plugin.h | 133 ++ include/plugin_browser.h | 96 ++ include/side_bar_widget.h | 8 +- include/song_editor.h | 1 - include/string_pair_drag.h | 64 + include/track.h | 9 +- include/track_container.h | 24 +- include/volume.h | 3 +- plugins/Makefile.am | 2 + plugins/audio_file_processor/Makefile.am | 34 + plugins/audio_file_processor/artwork.png | Bin 0 -> 26907 bytes .../audio_file_processor.cpp | 628 +++++++++ .../audio_file_processor.h | 116 ++ plugins/audio_file_processor/logo.png | Bin 0 -> 2249 bytes plugins/audio_file_processor/loop_off.png | Bin 0 -> 553 bytes plugins/audio_file_processor/loop_on.png | Bin 0 -> 933 bytes plugins/audio_file_processor/reverse_off.png | Bin 0 -> 478 bytes plugins/audio_file_processor/reverse_on.png | Bin 0 -> 755 bytes plugins/plucked_string_synth/Makefile.am | 33 + plugins/plucked_string_synth/artwork.png | Bin 0 -> 40827 bytes plugins/plucked_string_synth/logo.png | Bin 0 -> 3444 bytes .../plucked_string_synth.cpp | 274 ++++ .../plucked_string_synth.h | 216 +++ plugins/triple_oscillator/Makefile.am | 33 + plugins/triple_oscillator/am_active.png | Bin 0 -> 756 bytes plugins/triple_oscillator/am_inactive.png | Bin 0 -> 801 bytes plugins/triple_oscillator/artwork.png | Bin 0 -> 24224 bytes plugins/triple_oscillator/btn_mask.png | Bin 0 -> 187 bytes plugins/triple_oscillator/fm_active.png | Bin 0 -> 694 bytes plugins/triple_oscillator/fm_inactive.png | Bin 0 -> 715 bytes plugins/triple_oscillator/logo.png | Bin 0 -> 3536 bytes plugins/triple_oscillator/mix_active.png | Bin 0 -> 754 bytes plugins/triple_oscillator/mix_inactive.png | Bin 0 -> 802 bytes plugins/triple_oscillator/sync_active.png | Bin 0 -> 851 bytes plugins/triple_oscillator/sync_inactive.png | Bin 0 -> 965 bytes .../triple_oscillator/triple_oscillator.cpp | 1222 +++++++++++++++++ plugins/triple_oscillator/triple_oscillator.h | 167 +++ plugins/vestige/Makefile.am | 35 + plugins/vestige/artwork.png | Bin 0 -> 11857 bytes plugins/vestige/logo.png | Bin 0 -> 3593 bytes plugins/vestige/vestige.cpp | 860 ++++++++++++ plugins/vestige/vestige.h | 110 ++ resources/add_sample_track.png | Bin 683 -> 1978 bytes resources/project_export.png | Bin 1000 -> 1528 bytes resources/project_new.png | Bin 1144 -> 1874 bytes resources/project_open.png | Bin 1039 -> 2971 bytes resources/project_save.png | Bin 820 -> 1142 bytes resources/project_saveas.png | Bin 1135 -> 2080 bytes resources/sample_track.png | Bin 271 -> 1817 bytes src/audio/audio_alsa.cpp | 4 +- src/audio/audio_device.cpp | 10 +- src/audio/audio_file_ogg.cpp | 4 +- src/audio/audio_file_wave.cpp | 4 +- src/audio/audio_jack.cpp | 13 +- src/audio/audio_oss.cpp | 4 +- src/audio/audio_sdl.cpp | 4 +- src/core/bb_editor.cpp | 8 - src/core/config_mgr.cpp | 15 +- src/core/envelope_tab_widget.cpp | 3 - src/core/instrument.cpp | 93 ++ src/core/main.cpp | 1 + src/core/mixer.cpp | 21 - src/core/plugin.cpp | 175 +++ src/core/plugin_browser.cpp | 248 ++++ src/core/preset_preview_play_handle.cpp | 2 +- src/core/song_editor.cpp | 49 +- src/core/track.cpp | 16 +- src/core/track_container.cpp | 94 +- src/lib/buffer_allocator.cpp | 2 +- src/lib/embed.cpp | 19 +- src/lib/ladspa_manager.cpp | 26 +- src/lib/string_pair_drag.cpp | 112 ++ src/widgets/pixmap_button.cpp | 5 +- 96 files changed, 5262 insertions(+), 234 deletions(-) create mode 100644 buildtools/Makefile.am create mode 100644 include/dummy_instrument.h create mode 100644 include/dummy_plugin.h create mode 100644 include/plugin.h create mode 100644 include/plugin_browser.h create mode 100644 include/string_pair_drag.h create mode 100644 plugins/Makefile.am create mode 100644 plugins/audio_file_processor/Makefile.am create mode 100644 plugins/audio_file_processor/artwork.png create mode 100644 plugins/audio_file_processor/audio_file_processor.cpp create mode 100644 plugins/audio_file_processor/audio_file_processor.h create mode 100644 plugins/audio_file_processor/logo.png create mode 100644 plugins/audio_file_processor/loop_off.png create mode 100644 plugins/audio_file_processor/loop_on.png create mode 100644 plugins/audio_file_processor/reverse_off.png create mode 100644 plugins/audio_file_processor/reverse_on.png create mode 100644 plugins/plucked_string_synth/Makefile.am create mode 100644 plugins/plucked_string_synth/artwork.png create mode 100644 plugins/plucked_string_synth/logo.png create mode 100644 plugins/plucked_string_synth/plucked_string_synth.cpp create mode 100644 plugins/plucked_string_synth/plucked_string_synth.h create mode 100644 plugins/triple_oscillator/Makefile.am create mode 100644 plugins/triple_oscillator/am_active.png create mode 100644 plugins/triple_oscillator/am_inactive.png create mode 100644 plugins/triple_oscillator/artwork.png create mode 100644 plugins/triple_oscillator/btn_mask.png create mode 100644 plugins/triple_oscillator/fm_active.png create mode 100644 plugins/triple_oscillator/fm_inactive.png create mode 100644 plugins/triple_oscillator/logo.png create mode 100644 plugins/triple_oscillator/mix_active.png create mode 100644 plugins/triple_oscillator/mix_inactive.png create mode 100644 plugins/triple_oscillator/sync_active.png create mode 100644 plugins/triple_oscillator/sync_inactive.png create mode 100644 plugins/triple_oscillator/triple_oscillator.cpp create mode 100644 plugins/triple_oscillator/triple_oscillator.h create mode 100644 plugins/vestige/Makefile.am create mode 100644 plugins/vestige/artwork.png create mode 100644 plugins/vestige/logo.png create mode 100644 plugins/vestige/vestige.cpp create mode 100644 plugins/vestige/vestige.h create mode 100644 src/core/instrument.cpp create mode 100644 src/core/plugin.cpp create mode 100644 src/core/plugin_browser.cpp create mode 100644 src/lib/string_pair_drag.cpp diff --git a/ChangeLog b/ChangeLog index bf222c17b..eaebadb61 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,144 @@ +2005-10-19 Tobias Doerffel + + * plugins/vestige/vestige.h: + * plugins/vestige/vestige.cpp: + added possibility of opening plugins instead of hardcoding them ;-) + + * include/track.h: + renamed createTrack() and cloneTrack() to create() and clone() + + * include/track_container.h: + * src/core/track_container.cpp: + receive drop-events -> add channel with instrument/preset + + * src/audio/audio_jack.cpp: + removed usleep() out of loop in audioJACK::writeBufferToDev() since it + caused LMMS to hang e.g when removing a track + + * src/core/plugin_browser.cpp: + show hand-cursor if over a plugin-description-widget + + * src/widgets/pixmap_button.cpp: + if pixmap-button is set non-checkable, draw active-graphic if pressed + down and inactive one in normal state + + * include/lmms_main_win.h: + * src/core/lmms_main_win.cpp: + own workspace for making wheelEvent(...) is now obsolete -> removed + + * include/track_container.h: + * src/core/song_editor.cpp: + * src/core/track_container.cpp: + added own scroll-area for capturing special wheel-events where a + modifier-key (shift, control etc.) is pressed + + * src/core/song_editor.cpp: + removed add-channel-button as it is obsolete after adding plugin-browser + + * resources/: + improved icons such as project_*.png and sample-track-related icons + +2005-10-18 Tobias Doerffel + + * include/channel_track.h: + * src/tracks/channel_track.cpp: + - moved channelButton-implementation to channel_track.src + - added support for receiving drop-events, which makes channel-track + to load either the given instrument or the given preset + + * src/core/plugin_browser.cpp: + * include/plugin_browser.h: + added cool plugin-browser, which displays all available instrument- + plugins which are draggable to a channel-window/button + + * include/string_pair_drag.h: + * src/lib/string_pair_drag.cpp: + added drag'n'drop-implementation for dragging string-pairs (key/value) + which provides a standard-interface, although drag'n'drop has changed a + lot in Qt 4 + + * src/widgets/crystal_button.cpp: + made mouseMoveEvent()-method much more effective + + * Makefile.am: + * buildtools/Makefile.am: + build buildtools in subdir instead of top-build-directory - solves + dependendy problems with bin2res + + * src/core/file_browser.cpp: + do not depend on typeinfo of audioFileProcessor anymore by using + new setParameter()-method + + * include/plugin.h: + added setParameter() and getParameter()-methods for making LMMS able to + set parameters of a specific plugin without knowing anything about it + +2005-10-17 Tobias Doerffel + + * buildtools/bin2res.cpp: + * include/embed.h: + * src/lib/embed.cpp: + - declaration of embedded-data-descriptor is now located in embed.h and + part of namespace embed + - added support for local embedded-resources which is important for + plugins containing their own images etc., so data and access-methods + are stored into namespace PLUGIN_NAME + + * include/plugin.h: + * src/core/plugin.cpp: + added logo-field to descriptor-structure and simplified method for + getting descriptors of all plugins + + * include/basic_filters.h: + removed Moog-2-filter as it is only very CPU-intensive without any + significant difference to sound of normal Moog-filter + +2005-10-16 Tobias Doerffel + + * src/lib/ladspa_manager.cpp: + skip not existing/unreadable directories when searching for plugins + + * include/vestige.h: + * src/plugins/vestige.cpp: + make use of new instrument-play-handle + + * include/channel_track.h: + * src/core/browser.cpp: + * src/core/song_editor.cpp: + * src/midi/midi_file.cpp: + * src/tracks/channel_track.cpp: + renamed loadPlugin() to loadInstrument() + + * include/config_mgr.h: + * src/core/config_mgr.cpp: + added pluginDir()-method + + * include/instrument_play_handle.h: + added another play-handle for playing instruments which do not + produce sound for each note + + * src/plugins/: + renamed directory soundgenerators to plugins and modified all plugins + for working with revised plugin-system + + * include/instrument.h: + * include/plugin.h: + * src/core/instrument.cpp: + * src/core/plugin.cpp: + splitted code from instrument up into class plugin and class instrument + and revised plugin-system + + * include/instrument.h: + * src/core/instrument.cpp: + renamed files soundgenerator.* to instrument.* as well as class-name + +2005-10-15 Tobias Doerffel + + * include/vestige.h: + * src/soundgenerators/vestige.cpp: + new plugin "VeSTige" for handling VST-plugins - VERY experimental, + but at least, we get some sound out of these strange dll-files ;-) + 2005-10-13 Tobias Doerffel * src/audio/audio_jack.cpp: diff --git a/Makefile.am b/Makefile.am index 8ec5213bf..736133edf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,24 +19,23 @@ rpm: $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE).spec endif -noinst_PROGRAMS= bin2res -bin2res_SOURCES = buildtools/bin2res.cpp + +SUBDIRS = artwork buildtools locale midi-maps plugins presets projects samples INCLUDES = -I$(srcdir)/include -I. bin_PROGRAMS = lmms - AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) %.moc: $(srcdir)/include/%.h $(MOC) -o $@ $< - + %.ts: - $(LUPDATE) $(lmms_SOURCES) -ts locale/$@ - + $(LUPDATE) $(lmms_SOURCES) `find plugins/ -type f -name *.cpp` -ts locale/$@ + %.qm: %.ts $(LRELEASE) $< @@ -45,20 +44,18 @@ AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) lmms_MOC = \ ./about_dialog.moc \ ./arp_and_chords_tab_widget.moc \ - ./audio_file_processor.moc \ ./bb_editor.moc \ ./bb_track.moc \ - ./browser.moc \ ./channel_track.moc \ ./config_mgr.moc \ ./crystal_button.moc \ ./envelope_and_lfo_widget.moc \ ./envelope_tab_widget.moc \ ./export_project_dialog.moc \ - ./kmultitabbar.moc \ + ./file_browser.moc \ ./group_box.moc \ + ./kmultitabbar.moc \ ./knob.moc \ - ./ladspa_sine_1063.moc \ ./lcd_spinbox.moc \ ./led_checkbox.moc \ ./lmms_main_win.moc \ @@ -69,6 +66,7 @@ lmms_MOC = \ ./piano_roll.moc \ ./piano_widget.moc \ ./pixmap_button.moc \ + ./plugin_browser.moc \ ./project_notes.moc \ ./rename_dialog.moc \ ./sample_buffer.moc \ @@ -77,7 +75,6 @@ lmms_MOC = \ ./side_bar.moc \ ./side_bar_widget.moc \ ./song_editor.moc \ - ./sound_generator.moc \ ./surround_area.moc \ ./tab_bar.moc \ ./tab_button.moc \ @@ -86,7 +83,6 @@ lmms_MOC = \ ./timeline.moc \ ./track_container.moc \ ./track.moc \ - ./triple_oscillator.moc \ ./visualization_widget.moc @@ -94,8 +90,8 @@ BUILT_SOURCES = $(lmms_MOC) lmms_EMBEDDED_RESOURCES = $(wildcard $(srcdir)/resources/*png AUTHORS COPYING) -./embedded_resources.h: $(lmms_EMBEDDED_RESOURCES) bin2res - $(top_builddir)/bin2res $(lmms_EMBEDDED_RESOURCES) > $@ +./embedded_resources.h: $(lmms_EMBEDDED_RESOURCES) + $(top_builddir)/buildtools/bin2res $(lmms_EMBEDDED_RESOURCES) > $@ ./embed.o: ./embedded_resources.h @@ -113,11 +109,12 @@ lmms_SOURCES = \ $(srcdir)/src/core/about_dialog.cpp \ $(srcdir)/src/core/arp_and_chords_tab_widget.cpp \ $(srcdir)/src/core/bb_editor.cpp \ - $(srcdir)/src/core/browser.cpp \ $(srcdir)/src/core/config_mgr.cpp \ $(srcdir)/src/core/envelope_and_lfo_widget.cpp \ $(srcdir)/src/core/envelope_tab_widget.cpp \ $(srcdir)/src/core/export_project_dialog.cpp \ + $(srcdir)/src/core/file_browser.cpp \ + $(srcdir)/src/core/instrument.cpp \ $(srcdir)/src/core/lmms_main_win.cpp \ $(srcdir)/src/core/main.cpp \ $(srcdir)/src/core/mixer.cpp \ @@ -126,11 +123,12 @@ lmms_SOURCES = \ $(srcdir)/src/core/note_play_handle.cpp \ $(srcdir)/src/core/piano_roll.cpp \ $(srcdir)/src/core/piano_widget.cpp \ + $(srcdir)/src/core/plugin.cpp \ + $(srcdir)/src/core/plugin_browser.cpp \ $(srcdir)/src/core/preset_preview_play_handle.cpp \ $(srcdir)/src/core/sample_play_handle.cpp \ $(srcdir)/src/core/setup_dialog.cpp \ $(srcdir)/src/core/song_editor.cpp \ - $(srcdir)/src/core/sound_generator.cpp \ $(srcdir)/src/core/track.cpp \ $(srcdir)/src/core/track_container.cpp \ $(srcdir)/src/core/surround_area.cpp \ @@ -142,6 +140,7 @@ lmms_SOURCES = \ $(srcdir)/src/lib/mmp.cpp \ $(srcdir)/src/lib/oscillator.cpp \ $(srcdir)/src/lib/sample_buffer.cpp \ + $(srcdir)/src/lib/string_pair_drag.cpp \ $(srcdir)/src/midi/midi_alsa_raw.cpp \ $(srcdir)/src/midi/midi_device.cpp \ $(srcdir)/src/midi/midi_file.cpp \ @@ -189,16 +188,14 @@ lmms_SOURCES = \ $(srcdir)/include/volume.h \ $(srcdir)/include/panning.h \ $(srcdir)/include/song_editor.h \ - $(srcdir)/include/sound_generator.h \ - $(srcdir)/include/audio_file_processor.h \ + $(srcdir)/include/plugin.h \ + $(srcdir)/include/instrument.h \ $(srcdir)/include/midi_time.h \ $(srcdir)/include/bb_editor.h \ $(srcdir)/include/piano_widget.h \ $(srcdir)/include/effect_board.h \ $(srcdir)/include/pixmap_button.h \ - $(srcdir)/include/triple_oscillator.h \ $(srcdir)/include/settings.h \ - $(srcdir)/include/plucked_string_synth.h \ $(srcdir)/include/rename_dialog.h \ $(srcdir)/include/export_project_dialog.h \ $(srcdir)/include/note_play_handle.h \ @@ -214,7 +211,8 @@ lmms_SOURCES = \ $(srcdir)/include/group_box.h \ $(srcdir)/include/tab_widget.h \ $(srcdir)/include/knob.h \ - $(srcdir)/include/browser.h \ + $(srcdir)/include/file_browser.h \ + $(srcdir)/include/plugin_browser.h \ $(srcdir)/include/templates.h \ $(srcdir)/include/gui_templates.h \ $(srcdir)/include/surround_area.h \ @@ -248,7 +246,6 @@ lmms_SOURCES = \ $(srcdir)/include/preset_preview_play_handle.h \ $(srcdir)/include/sample_play_handle.h \ $(srcdir)/include/midi.h \ - $(srcdir)/include/midi_out.h \ $(srcdir)/include/nstate_button.h \ $(srcdir)/include/midi_dummy.h \ $(srcdir)/include/midi_mapper.h \ @@ -259,7 +256,10 @@ lmms_SOURCES = \ $(srcdir)/include/text_float.h \ $(srcdir)/include/tempo_sync_knob.h \ $(srcdir)/include/setup_dialog.h \ - $(srcdir)/include/empty_sg_plugin.h \ + $(srcdir)/include/dummy_plugin.h \ + $(srcdir)/include/dummy_instrument.h \ + $(srcdir)/include/instrument_play_handle.h \ + $(srcdir)/include/string_pair_drag.h \ $(srcdir)/include/ladspa_manager.h @@ -296,18 +296,6 @@ if HAVE_LIBSF LIB_SF_LDADD = -lsndfile endif -lmms_LDADD = $(QT_LDADD) $(LIB_SDL_LDADD) $(LIB_ASOUND_LDADD) $(LIB_JACK_LDADD) $(LIB_SDL_SOUND_LDADD) $(LIB_VORBIS_LDADD) $(LIB_SRC_LDADD) $(LIB_SF_LDADD) -laudiofileprocessor -ldl +lmms_LDADD = $(QT_LDADD) $(LIB_SDL_LDADD) $(LIB_ASOUND_LDADD) $(LIB_JACK_LDADD) $(LIB_SDL_SOUND_LDADD) $(LIB_VORBIS_LDADD) $(LIB_SRC_LDADD) $(LIB_SF_LDADD) -lfst -ldl lmms_LDFLAGS = -rdynamic -rpath $(pkglibdir) -SUBDIRS = artwork locale midi-maps presets projects samples - - - -pkglib_LTLIBRARIES= libaudiofileprocessor.la libsine1063oscillator.la libmidiout.la libpluckedstringsynth.la libtripleoscillator.la - - -libaudiofileprocessor_la_SOURCES = src/soundgenerators/audio_file_processor.cpp -libsine1063oscillator_la_SOURCES = src/soundgenerators/ladspa_sine_1063.cpp -libmidiout_la_SOURCES = src/soundgenerators/midi_out.cpp -libpluckedstringsynth_la_SOURCES = src/soundgenerators/plucked_string_synth.cpp -libtripleoscillator_la_SOURCES = src/soundgenerators/triple_oscillator.cpp diff --git a/README b/README index 46708f92b..8c6932c87 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -Linux MultiMedia Studio 0.1.2 +Linux MultiMedia Studio 0.1.1 ============================== Copyright (c) 2004-2005 by Tobias Doerffel and others @@ -41,13 +41,13 @@ Requirements ------------ The most important requirement is for sure a fast computer, so don't try to get -LMMS working on a pentium I with 60 MHz... ;-) So you should have at least -500 MHz, but for really enjoying LMMS less than 1 GHz makes no sense... -(LMMS is currently developed on a PIII 450 MHz...!!) +LMMS working on a pentium I with 60 MHz... ;-) Therefore you should have at +least 500 MHz, but for really enjoying LMMS less than 1 GHz makes no sense... + Required libraries are: -- Qt 3.2 or higher (tested up to 4.0.0) with devel-files +- Qt 3.0 (3.2 recommended) or higher (tested up to 4.0.0) with devel-files Optional, but strongly recommended: - libvorbis with devel-files @@ -57,6 +57,7 @@ Optional, but strongly recommended: - libsamplerate with devel-files - libsndfile with devel-files - JACK with devel-files +- libfst + header-files from Steinberg SDK For compiling you should have an up to date GCC with g++. LMMS has been (successfully) tested under Debian Sarge 3.1, Fedora Core 2-4, @@ -83,16 +84,16 @@ can be found at http://lmms.sourceforge.net -Before coding a new big feature, please always post your idea and suggestions +Details on development can be found at + +http://lmms.sourceforge.net/development.php + +or in the Wiki: + +http://wiki.mindrules.net + +Before coding a new big feature, please ALWAYS post your idea and suggestions about your feature and about the actual implementation to the LMMS-devel-mailinglist (lmms-devel@lists.sourceforge.net) and wait for replies! Maybe there're different ideas, improvements, hints or maybe your feature is not welcome/needed at the moment (but for sure this will be very seldom). - -If you coded your feature, make sure, that it is running properly with the -newest available version of LMMS and that it also runs with different -configurations (e.g. disabled surround-support, missing package(s) etc.). -Important is also, that you comment your source so that other people can fix -bugs or improve your feature! - - diff --git a/TODO b/TODO index d97492ddd..75a0d9146 100644 --- a/TODO +++ b/TODO @@ -1,18 +1,20 @@ -- toolbar-redesign in song-editor, bb-editor and piano-roll!!! -- addchannel-toolbutton -> popup-menu with available soundgenerator-plugins +- complete toolbar-redesign in song-editor, bb-editor and piano-roll!!! +- dnd everywhere: presets, samples (afp/sample-track), TCO's, knob-values +- save/load parameters of VST-plugin +- move VST-code into separate class which can use several backends (libfst, dssi-vst and vst-server) -> add libfst/dssi-vst/vstserver-check to configure.in +- somehow avoid hidden plugin-descriptor-widgets if height of window is too small -> add scrollbar +- use drawLineF() for drawing notes in pattern::paintEvent() in qt4-version +- pattern freeze -> do not endless loop if looping-points are enabled - solve problem with knob-control-precision -- built-in VST-support -- proper dlclos()ing of sg-plugins -- use own scrollview for capturing wheel-events - add note-len- and note-alignment-selectbox to piano-roll - only redraw region given by paint-event in pattern, bbTCO, sampleTCO etc. - make LMMS an ALSA-sequencer-client + - use midi-maps + - process program-/channel-change-events from MIDI-files + - setup MIDI-channel and -program in MIDI-Out - pre-listen when opening sample with QFileDialog - level-meters in output-graph and channel-track - panning-editing in piano-roll -- use midi-maps -- process program-/channel-change-events from MIDI-files -- setup MIDI-channel and -program in MIDI-Out - speed up painting of sampleTCO - save window-positions, -states and -sizes in files - solve problems with different keyboard-layouts when playing channel-track with pc-keyboard -> use tr() diff --git a/buildtools/Makefile.am b/buildtools/Makefile.am new file mode 100644 index 000000000..78630dc92 --- /dev/null +++ b/buildtools/Makefile.am @@ -0,0 +1,5 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + +noinst_PROGRAMS= bin2res +bin2res_SOURCES = bin2res.cpp + diff --git a/buildtools/bin2res.cpp b/buildtools/bin2res.cpp index 9680c699c..4570096d9 100644 --- a/buildtools/bin2res.cpp +++ b/buildtools/bin2res.cpp @@ -97,7 +97,7 @@ int main( int argc, char * * argv ) e->cname = convertFileNameToCIdentifier( e->name ); embedded_data.push_back( e ); std::string s; - std::cout << "static const unsigned char " << e->cname << + std::cout << "const unsigned char " << e->cname << "_data[] = {"; embedData( data, fsize, std::cout ); std::cout << std::endl << "};" << std::endl << std::endl; @@ -106,7 +106,7 @@ int main( int argc, char * * argv ) if( embedded_data.size() > 0 ) { - std::cout << "static const unsigned char dummy_data[] =" + std::cout << "const unsigned char dummy_data[] =" "{ 0x00 };" << std::endl << std::endl; embed * dummy = new embed; dummy->size = 1; @@ -114,14 +114,15 @@ int main( int argc, char * * argv ) dummy->cname = convertFileNameToCIdentifier( "dummy" ); embedded_data.push_back( dummy ); - std::cout << "#include " << std::endl; - std::cout << "static struct embedDesc" << std::endl - << "{" << std::endl + std::cout << "#include " << std::endl << std::endl; + std::cout << "#include \"embed.h\"" << std::endl << std::endl; + std::cout << "embed::descriptor embed_vec[] = {" << std::endl; +/* << "{" << std::endl << " int size;" << std::endl << " const unsigned char * data;" << std::endl << " const char * name;" << std::endl - << "} embed_vec[] = {" << std::endl; + << "} embed_vec[] = {" << std::endl;*/ while( embedded_data.size() > 0 ) { embed * e = embedded_data[0]; @@ -133,9 +134,9 @@ int main( int argc, char * * argv ) } std::cout << " { 0, 0, 0 }" << std::endl << "};" << std::endl << std::endl - << "static const embedDesc & findEmbeddedData( " - "const char * _name )" << std::endl - << "{" << std::endl + << "const embed::descriptor & " + "findEmbeddedData( const char * _name )" + << std::endl << "{" << std::endl << " for( int i = 0; embed_vec[i].data; " "i++ )" << std::endl << " {" << std::endl diff --git a/configure.in b/configure.in index 31ed5a8d2..b2cafdf18 100644 --- a/configure.in +++ b/configure.in @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT(lmms, 0.1.1-cvs20051013, tobydox@users.sourceforge.net) -AM_INIT_AUTOMAKE(lmms, 0.1.1-cvs20051013) +AC_INIT(lmms, 0.1.1-cvs20051019, tobydox@users.sourceforge.net) +AM_INIT_AUTOMAKE(lmms, 0.1.1-cvs20051019) AM_CONFIG_HEADER(config.h) @@ -22,7 +22,7 @@ gw_CHECK_QT # checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([fcntl.h memory.h string.h sys/ioctl.h unistd.h stdlib.h dlfcn.h]) +AC_CHECK_HEADERS([fcntl.h memory.h string.h sys/ioctl.h unistd.h stdlib.h dlfcn.h ladspa.h]) # checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -349,14 +349,23 @@ fi AC_CONFIG_FILES([Makefile artwork/Makefile + buildtools/Makefile locale/Makefile midi-maps/Makefile + plugins/Makefile + plugins/audio_file_processor/Makefile + plugins/ladspa_sine_1063/Makefile + plugins/midi_out/Makefile + plugins/plucked_string_synth/Makefile + plugins/triple_oscillator/Makefile + plugins/vestige/Makefile presets/Makefile presets/AudioFileProcessor/Makefile presets/MIDI-Out/Makefile presets/PluckedStringSynth/Makefile presets/Sine1063Oscillator/Makefile presets/TripleOscillator/Makefile + presets/VeSTige/Makefile projects/Makefile projects/cool_songs/Makefile projects/covers/Makefile diff --git a/include/audio_alsa.h b/include/audio_alsa.h index 0604724e2..aa4cdf808 100644 --- a/include/audio_alsa.h +++ b/include/audio_alsa.h @@ -80,7 +80,7 @@ public: private: virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ); + float _master_gain ); int FASTCALL setHWParams( Uint32 _sample_rate, Uint32 _channels, snd_pcm_access_t _access ); diff --git a/include/audio_device.h b/include/audio_device.h index 28f612e07..0bf7dd980 100644 --- a/include/audio_device.h +++ b/include/audio_device.h @@ -68,7 +68,7 @@ public: void FASTCALL writeBuffer( surroundSampleFrame * _ab, Uint32 _frames, Uint32 _src_sample_rate, - float _master_output ); + float _master_gain ); inline Uint32 sampleRate( void ) const { @@ -104,12 +104,12 @@ public: protected: virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ) = 0; + float _master_gain ) = 0; // convert a given audio-buffer to a buffer in signed 16-bit samples // returns num of bytes in outbuf int FASTCALL convertToS16( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output, + float _master_gain, outputSampleType * _output_buffer, bool _convert_endian = FALSE ); diff --git a/include/audio_file_ogg.h b/include/audio_file_ogg.h index 2a1ae256c..9ab234932 100644 --- a/include/audio_file_ogg.h +++ b/include/audio_file_ogg.h @@ -64,7 +64,7 @@ public: private: virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ); + float _master_gain ); bool startEncoding( void ); void finishEncoding( void ); diff --git a/include/audio_file_wave.h b/include/audio_file_wave.h index c8e95a033..61358a0e9 100644 --- a/include/audio_file_wave.h +++ b/include/audio_file_wave.h @@ -57,7 +57,7 @@ public: private: virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ); + float _master_gain ); bool startEncoding( void ); void finishEncoding( void ); diff --git a/include/audio_jack.h b/include/audio_jack.h index df7eaa943..4b33b79eb 100644 --- a/include/audio_jack.h +++ b/include/audio_jack.h @@ -91,7 +91,7 @@ public: private: virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ); + float _master_gain ); void clearBuffer( void ); diff --git a/include/audio_oss.h b/include/audio_oss.h index 49c8bcf6b..443a2bcd4 100644 --- a/include/audio_oss.h +++ b/include/audio_oss.h @@ -75,7 +75,7 @@ public: private: virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ); + float _master_gain ); int m_audioFD; diff --git a/include/audio_sample_recorder.h b/include/audio_sample_recorder.h index 8bbfc47d0..31a2af9cc 100644 --- a/include/audio_sample_recorder.h +++ b/include/audio_sample_recorder.h @@ -63,7 +63,7 @@ public: private: virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ); + float _master_gain ); typedef vvector > bufferVector; bufferVector m_buffers; diff --git a/include/audio_sdl.h b/include/audio_sdl.h index 1f8670252..6c6cf890a 100644 --- a/include/audio_sdl.h +++ b/include/audio_sdl.h @@ -88,7 +88,7 @@ public: private: virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ); + float _master_gain ); void clearBuffer( void ); diff --git a/include/basic_filters.h b/include/basic_filters.h index 5f6bbf306..7d02c0efd 100644 --- a/include/basic_filters.h +++ b/include/basic_filters.h @@ -54,11 +54,9 @@ public: NOTCH, ALLPASS, MOOG, - MOOG2, SIMPLE_FLT_CNT, DOUBLE_LOWPASS = 16+LOWPASS, - DOUBLE_MOOG = 16+MOOG, - DOUBLE_MOOG2 = 16+MOOG2 + DOUBLE_MOOG = 16+MOOG } ; static inline filterTypes getFilterType( const int _idx ) @@ -127,7 +125,7 @@ public: m_y4[_chnl] * ( 1.0f / 6.0f ); break; } - case MOOG2: +/* case MOOG2: case DOUBLE_MOOG2: { const float x1 = ( _in0 - m_r * @@ -190,7 +188,7 @@ public: out = m_oldx[_chnl]; break; - } + }*/ default: // filter @@ -251,7 +249,7 @@ public: break; } - case DOUBLE_MOOG2: +/* case DOUBLE_MOOG2: { if( m_subFilter == NULL ) { @@ -276,7 +274,7 @@ public: kfcr * kf ) ); m_r = 4 * _q * kacr; break; - } + }*/ default: { diff --git a/include/bb_editor.h b/include/bb_editor.h index eea12f814..f3d4b46b8 100644 --- a/include/bb_editor.h +++ b/include/bb_editor.h @@ -83,10 +83,10 @@ public: protected: - void closeEvent( QCloseEvent * _ce ); - void keyPressEvent (QKeyEvent * _ke); - void wheelEvent( QWheelEvent * _we ); - void resizeEvent( QResizeEvent * _re ); + virtual void closeEvent( QCloseEvent * _ce ); + virtual void keyPressEvent( QKeyEvent * _ke ); + virtual void resizeEvent( QResizeEvent * _re ); + void updateBackground( void ); diff --git a/include/config_mgr.h b/include/config_mgr.h index 0143aac28..89afa1444 100644 --- a/include/config_mgr.h +++ b/include/config_mgr.h @@ -105,6 +105,11 @@ public: { return( m_lmmsDataDir + LOCALE_PATH ); } + const QString & pluginDir( void ) const + { + return( m_lmmsPluginDir ); + } + const QString & value( const QString & _class, const QString & _attribute ) const; @@ -148,6 +153,7 @@ private: const QString m_lmmsRcFile; QString m_lmmsWorkingDir; QString m_lmmsDataDir; + QString m_lmmsPluginDir; typedef vvector > stringPairVector; typedef QMap settingsMap; diff --git a/include/dummy_instrument.h b/include/dummy_instrument.h new file mode 100644 index 000000000..c8af94104 --- /dev/null +++ b/include/dummy_instrument.h @@ -0,0 +1,61 @@ +/* + * dummy_instrument.h - instrument used as fallback if an instrument couldn't + * be loaded + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _DUMMY_INSTRUMENT_H +#define _DUMMY_INSTRUMENT_H + +#include "instrument.h" + + +class dummyInstrument : public instrument +{ +public: + inline dummyInstrument( channelTrack * _channel_track ) : + instrument( _channel_track, "Dummy instrument" ) + { + } + + inline virtual ~dummyInstrument() + { + } + + + inline virtual void saveSettings( QDomDocument &, QDomElement & ) + { + } + + inline virtual void loadSettings( const QDomElement & ) + { + } + + inline virtual QString nodeName( void ) const + { + return( "dummyinstrument" ); + } + +} ; + + +#endif diff --git a/include/dummy_plugin.h b/include/dummy_plugin.h new file mode 100644 index 000000000..f84e44af4 --- /dev/null +++ b/include/dummy_plugin.h @@ -0,0 +1,61 @@ +/* + * dummy_plugin.h - empty plugin which is used as fallback if a plugin wasn't + * found + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _DUMMY_PLUGIN_H +#define _DUMMY_PLUGIN_H + +#include "plugin.h" + + +class dummyPlugin : public plugin +{ +public: + inline dummyPlugin( void ) : + plugin( "Dummy plugin", plugin::UNDEFINED ) + { + } + + inline virtual ~dummyPlugin() + { + } + + + inline virtual void saveSettings( QDomDocument &, QDomElement & ) + { + } + + inline virtual void loadSettings( const QDomElement & ) + { + } + + inline virtual QString nodeName( void ) const + { + return( "dummyplugin" ); + } + +} ; + + +#endif diff --git a/include/embed.h b/include/embed.h index 314c8ccd4..1fb6878d7 100644 --- a/include/embed.h +++ b/include/embed.h @@ -42,10 +42,31 @@ namespace embed { +struct descriptor +{ + int size; + const unsigned char * data; + const char * name; +} ; + + QPixmap getIconPixmap( const char * _name, int _w = -1, int _h = -1 ); QString getText( const char * _name ); void loadTranslation( const QString & _tname ); } + +#ifdef PLUGIN_NAME +namespace PLUGIN_NAME +{ + +QPixmap getIconPixmap( const char * _name, int _w = -1, int _h = -1 ); +QString getText( const char * _name ); +void loadTranslation( const QString & _tname ); + +} +#endif + + #endif diff --git a/include/ladspa_manager.h b/include/ladspa_manager.h index c514e3453..12c917bc5 100644 --- a/include/ladspa_manager.h +++ b/include/ladspa_manager.h @@ -31,6 +31,12 @@ #include #endif +#ifdef HAVE_LADSPA_H + +#define LADSPA_SUPPORT + +#include + #include "qt3support.h" #ifdef QT4 @@ -49,9 +55,10 @@ #endif -#include + #include "types.h" + typedef QPair ladspaKey; /* ladspaManager provides a database of LADSPA plug-ins. Upon instantiation, @@ -188,7 +195,7 @@ public: /* The following methods are convenience functions for use during - development. A real soundGenerator should use the getDescriptor() + development. A real instrument should use the getDescriptor() method and implement the plug-in manipulations internally to avoid the overhead associated with QMap lookups. */ @@ -326,5 +333,7 @@ private: typedef QMap ladspaManagerMapType; ladspaManagerMapType m_ladspaManagerMap; }; - + +#endif + #endif diff --git a/include/mixer.h b/include/mixer.h index 0a2353751..b6d52dfa1 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -57,6 +57,7 @@ class audioDevice; class midiDevice; class lmmsMainWin; +class plugin; const int DEFAULT_BUFFER_SIZE = 512; @@ -174,7 +175,8 @@ public: m_playHandlesToRemove.push_back( _ph ); } - void FASTCALL checkValidityOfPlayHandles( void ); + void checkValidityOfPlayHandles( void ); + inline int sampleRate( void ) @@ -319,7 +321,6 @@ private: volatile bool m_quit; - audioDevice * m_audioDev; audioDevice * m_oldAudioDev; QString m_audioDevName; diff --git a/include/plugin.h b/include/plugin.h new file mode 100644 index 000000000..95daa74d0 --- /dev/null +++ b/include/plugin.h @@ -0,0 +1,133 @@ +/* + * plugin.h - class plugin, the base-class and generic interface for all plugins + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PLUGIN_H +#define _PLUGIN_H + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "types.h" +#include "settings.h" +#include "embed.h" + + +#define STRINGIFY_PLUGIN_NAME(s) STR(s) +#define STR(PN) #PN + + +class QPixmap; + + +class plugin : public settings +{ +public: + enum pluginTypes + { + INSTRUMENT, // instrument being used in channel-track + EFFECT, // effect-plugin for effect-board + IMPORT_FILTER, // filter for importing a file + EXPORT_FILTER, // filter for exporting a file + UNDEFINED = 255 + } ; + + // descriptor holds information about a plugin - every external plugin + // has to instantiate such a descriptor in an extern "C"-section so that + // the plugin-loader is able to access information about the plugin + struct descriptor + { + const char * name; + const char * public_name; + const char * description; + const char * author; + int version; + pluginTypes type; + embed::descriptor logo; + } ; + + // contructor of a plugin + // _name: public name of plugin + // _type: one of the plugin-types defined above + plugin( const QString & _public_name, pluginTypes _type ); + virtual ~plugin(); + + // returns the name, the plugin passed to plugin-constructor + inline const QString & publicName( void ) const + { + return( m_publicName ); + } + + // return type + inline pluginTypes type( void ) const + { + return( m_type ); + } + + // plugins can overload this for making other classes able to change + // settings of the plugin without knowing the actual class + virtual void FASTCALL setParameter( const QString & _param, + const QString & _value ); + + // plugins can overload this for making other classes able to query + // settings of the plugin without knowing the actual class + virtual QString FASTCALL getParameter( const QString & _param ); + + + // returns an instance of a plugin whose name matches to given one + // if specified plugin couldn't be loaded, it creates a dummy-plugin + static plugin * FASTCALL instantiate( const QString & _plugin_name, + void * _data ); + + // fills given vector with descriptors for all available plugins + static void FASTCALL getDescriptorsOfAvailPlugins( + vvector & _plugin_descs ); + +private: + const QString m_publicName; + const pluginTypes m_type; + + // pointer to instantiation-function in plugin + typedef plugin * ( * instantiationHook )( void * ); + +} ; + + +#endif diff --git a/include/plugin_browser.h b/include/plugin_browser.h new file mode 100644 index 000000000..066c6a834 --- /dev/null +++ b/include/plugin_browser.h @@ -0,0 +1,96 @@ +/* + * plugin_browser.h - include file for pluginBrowser + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PLUGIN_BROWSER_H +#define _PLUGIN_BROWSER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "side_bar_widget.h" +#include "plugin.h" + + +class trackContainer; + + +class pluginBrowser : public sideBarWidget +{ + Q_OBJECT +public: + pluginBrowser( QWidget * _parent ); + virtual ~pluginBrowser(); + + +private: + vvector m_pluginDescriptors; + + QWidget * m_view; + +} ; + + + + +class pluginDescWidget : public QWidget +{ +public: + pluginDescWidget( const plugin::descriptor & _pd, QWidget * _parent ); + virtual ~pluginDescWidget(); + + +protected: + virtual void paintEvent( QPaintEvent * _pe ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + + +private: + const plugin::descriptor & m_pluginDescriptor; + QPixmap m_logo; + + bool m_mouseOver; + +} ; + + +#endif diff --git a/include/side_bar_widget.h b/include/side_bar_widget.h index 11032a954..ba059790c 100644 --- a/include/side_bar_widget.h +++ b/include/side_bar_widget.h @@ -48,7 +48,7 @@ class sideBarWidget : public QWidget public: sideBarWidget( const QString & _title, const QPixmap & _icon, QWidget * _parent ); - ~sideBarWidget(); + virtual ~sideBarWidget(); inline const QPixmap & icon( void ) const { return( m_icon ); @@ -60,9 +60,9 @@ public: protected: - void paintEvent( QPaintEvent * _pe ); - void resizeEvent( QResizeEvent * _re ); - inline void contextMenuEvent( QContextMenuEvent * ) + virtual void paintEvent( QPaintEvent * _pe ); + virtual void resizeEvent( QResizeEvent * _re ); + inline virtual void contextMenuEvent( QContextMenuEvent * ) { } diff --git a/include/song_editor.h b/include/song_editor.h index ea55094cb..3bfc8e00c 100644 --- a/include/song_editor.h +++ b/include/song_editor.h @@ -229,7 +229,6 @@ protected: protected slots: void insertTact( void ); void removeTact( void ); - void addChannelTrack( void ); void addBBTrack( void ); void addSampleTrack( void ); void scrolled( int _new_pos ); diff --git a/include/string_pair_drag.h b/include/string_pair_drag.h new file mode 100644 index 000000000..41b3df102 --- /dev/null +++ b/include/string_pair_drag.h @@ -0,0 +1,64 @@ +/* + * string_pair_drag.h - class stringPairDrag which provides general support + * for drag'n'drop of string-pairs + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef _STRING_PAIR_DRAG_H +#define _STRING_PAIR_DRAG_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +class QPixmap; + + +class stringPairDrag : public +#ifdef QT4 + QDrag +#else + QStoredDrag +#endif +{ +public: + stringPairDrag( const QString & _key, const QString & _value, + const QPixmap & _icon, QWidget * _w ); + ~stringPairDrag(); + + static void processDragEnterEvent( QDragEnterEvent * _dee, + const QString & _allowed_keys ); + static QString decodeKey( QDropEvent * _de ); + static QString decodeValue( QDropEvent * _de ); + +} ; + + +#endif diff --git a/include/track.h b/include/track.h index 39e2605b8..1245884ed 100644 --- a/include/track.h +++ b/include/track.h @@ -227,7 +227,7 @@ public: public slots: void changePosition( const midiTime & _new_pos = -1 ); void cloneTrack( void ); - void deleteTrack( void ); + void removeTrack( void ); void moveTrackUp( void ); void moveTrackDown( void ); void setMuted( bool _muted ); @@ -271,11 +271,10 @@ public: track( trackContainer * _tc ); virtual ~track(); - static track * FASTCALL createTrack( trackTypes _tt, + static track * FASTCALL create( trackTypes _tt, trackContainer * _tc ); + static track * FASTCALL create( const QDomElement & _this, trackContainer * _tc ); - static track * FASTCALL createTrack( const QDomElement & _this, - trackContainer * _tc ); - static track * FASTCALL cloneTrack( track * _track ); + static track * FASTCALL clone( track * _track ); tact length( void ) const; diff --git a/include/track_container.h b/include/track_container.h index 985f80d23..9ddd6fcce 100644 --- a/include/track_container.h +++ b/include/track_container.h @@ -93,10 +93,13 @@ public: protected: + virtual void resizeEvent( QResizeEvent * ); + virtual void dragEnterEvent( QDragEnterEvent * _dee ); + virtual void dropEvent( QDropEvent * _de ); + constTrackVector tracks( void ) const; trackVector tracks( void ); - virtual void resizeEvent( QResizeEvent * ); midiTime m_currentPosition; @@ -106,16 +109,33 @@ protected slots: private: - QScrollArea * m_scrollArea; + + class scrollArea : public QScrollArea + { + public: + scrollArea( trackContainer * _parent ); + virtual ~scrollArea(); + + protected: + virtual void wheelEvent( QWheelEvent * _we ); + + } ; + + + scrollArea * m_scrollArea; typedef vvector trackWidgetVector; trackWidgetVector m_trackWidgets; float m_ppt; + friend class scrollArea; + + signals: void positionChanged( const midiTime & _pos ); + } ; diff --git a/include/volume.h b/include/volume.h index dc37abd48..640c349d3 100644 --- a/include/volume.h +++ b/include/volume.h @@ -1,5 +1,6 @@ /* - * volume.h - declaration of some constants and types, concerning the volume of a note + * volume.h - declaration of some constants and types, concerning the volume + * of a note * * Linux MultiMedia Studio * Copyright (c) 2004-2005 Tobias Doerffel diff --git a/plugins/Makefile.am b/plugins/Makefile.am new file mode 100644 index 000000000..cd149a68c --- /dev/null +++ b/plugins/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = audio_file_processor ladspa_sine_1063 midi_out plucked_string_synth triple_oscillator vestige + diff --git a/plugins/audio_file_processor/Makefile.am b/plugins/audio_file_processor/Makefile.am new file mode 100644 index 000000000..3c8af8bc5 --- /dev/null +++ b/plugins/audio_file_processor/Makefile.am @@ -0,0 +1,34 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I. + + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="audiofileprocessor" + + +%.moc: ./%.h + $(MOC) -o $@ $< + + +MOC_FILES = ./audio_file_processor.moc + +BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h + +EMBEDDED_RESOURCES = $(wildcard *png) + +./embedded_resources.h: $(EMBEDDED_RESOURCES) + $(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@ + +EXTRA_DIST = $(EMBEDDED_RESOURCES) + + +CLEANFILES = $(MOC_FILES) ./embedded_resources.h + + + +pkglib_LTLIBRARIES= libaudiofileprocessor.la + +libaudiofileprocessor_la_SOURCES = audio_file_processor.cpp audio_file_processor.h + +$(libaudiofileprocessor_la_SOURCES): ./embedded_resources.h diff --git a/plugins/audio_file_processor/artwork.png b/plugins/audio_file_processor/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..6b980c2333a743ed3da76063d3df4cf7fce110ef GIT binary patch literal 26907 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

r$)(-}>% zLlo((gDKJR$*iWN%*j~hOe#Z!&l{I8D+}@;iLwaioXNyG!4$}#*2YXFD;o$KS5^IM z{M$eO3xF>H{^-(drn)^fwXvGV#eDvFJOahPB?jEA*AI_7phD^h%y4_Nxx3$53Yj!B zIaB}E?d^M3zkGz>ug+9@#qal9H@9~WTeD5*_GYtvc(Cufx!F8Ca{xY{yR_Np{Oh!` z7J8HRE^bzUzKUhEcgfrmuv#pi_Iq_TLxkmGzCRpnH~C_I z{dhdac5fsASj-puq?wV|{Q7!-INCqg{Q7!-I@x6A`l|Xs?haHK0VnZn24#uqOC&^B zLI^04fMiy3z{)d&rti{IBO4vH^7@|DFCVj_Rn-`@p8Ne;N647FSD`SDkVH`UKvzvJ zsS=X+bmh^Y_`-V%-nz=5cSQ(f^#IR&2hclgo$AUEA&meqXD%fDgnZmOzCh$y{g=J-a#Ax^~3x%)|{sKx^17#cg_ zV?-Q7klZ~c8bm|vOG3@v#Z`u_tBmCC&wK|)#WK;Gpw=1|OMc(9ntw0?dp|busLDE< zN%ijHYbh%f6BGs&Mhw5}k0mZtQxM6)*sl!)uDj7_fUGjw@ zc)AKUx<}726W!@qbSLi%yCR?bEV@(Y(KfWYvhd-&Gc`GcMOsvrMevg|gLBmPwu(y5 z(a=v0DP@WM%<>#`CG{>%m5n^77>0au0ig2r=-%~X-{(^VBA4yZG;$P|n_)QlF8$|N{z;>IBr|8M29ZzAE76@15os{dorp&1aGgf?$lY@iKx5MkQ_ryR zQgl}iqtQLGnirz`fG|2gCbc6+Ufv~o6QetK-Pp8h8Y1*&Y&%HCe+ct<&$oXS0;IFs%!If9O{F=Y@!C!yG;wSmVPq_YdzWgMM-{^u*{>IDyoqg1X=1+9- zr``1YDQHilT`Bvsv S?#F`w0000 + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include + +#define isChecked isOn +#define setChecked setOn + +#endif + + +#include "audio_file_processor.h" +#include "song_editor.h" +#include "channel_track.h" +#include "note_play_handle.h" +#include "paths.h" +#include "interpolation.h" +#include "buffer_allocator.h" +#include "pixmap_button.h" +#include "knob.h" +#include "tooltip.h" + +#include "embed.cpp" + + + +extern "C" +{ + +plugin::descriptor audiofileprocessor_plugin_descriptor = +{ + STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), + "AudioFileProcessor", + QT_TRANSLATE_NOOP( "plugin", + "simple sampler with various settings for " + "using samples (e.g. drums) in a channel" ), + "Tobias Doerffel ", + 0x0100, + plugin::INSTRUMENT, + PLUGIN_NAME::findEmbeddedData( "logo.png" ) +} ; + +} + + +QPixmap * audioFileProcessor::s_artwork = NULL; + + + +audioFileProcessor::audioFileProcessor( channelTrack * _channel_track ) : + instrument( _channel_track, + audiofileprocessor_plugin_descriptor.public_name ), + specialBgHandlingWidget( PLUGIN_NAME::getIconPixmap( "artwork" ) ), + m_sampleBuffer( "" ), + m_drawMethod( sampleBuffer::LINE_CONNECT ) +{ + connect( &m_sampleBuffer, SIGNAL( sampleUpdated() ), this, + SLOT( sampleUpdated() ) ); + + if( s_artwork == NULL ) + { + s_artwork = new QPixmap( PLUGIN_NAME::getIconPixmap( + "artwork" ) ); + } + + + m_openAudioFileButton = new pixmapButton( this ); + m_openAudioFileButton->setCheckable( FALSE ); + m_openAudioFileButton->setCursor( QCursor( Qt::PointingHandCursor ) ); + m_openAudioFileButton->move( 200, 90 ); + m_openAudioFileButton->setActiveGraphic( embed::getIconPixmap( + "project_open_down" ) ); + m_openAudioFileButton->setInactiveGraphic( embed::getIconPixmap( + "project_open" ) ); + m_openAudioFileButton->setBgGraphic( getBackground( + m_openAudioFileButton ) ); + connect( m_openAudioFileButton, SIGNAL( clicked() ), this, + SLOT( openAudioFile() ) ); + toolTip::add( m_openAudioFileButton, tr( "Open other sample" ) ); + +#ifdef QT4 + m_openAudioFileButton->setWhatsThis( +#else + QWhatsThis::add( m_openAudioFileButton, +#endif + tr( "Click here, if you want to open another audio-file. After " + "clicking on this button, a file-open-dialog appears " + "and you can select your file. Settings like Looping-" + "Mode, start- and end-point, amplify-value and so on " + "are not reset, so please don't wonder if your sample " + "doesn't sound like the original one..." ) ); + + m_reverseButton = new pixmapButton( this ); + m_reverseButton->move( 160, 124 ); + m_reverseButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "reverse_on" ) ); + m_reverseButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "reverse_off" ) ); + m_reverseButton->setBgGraphic( getBackground( m_reverseButton ) ); + connect( m_reverseButton, SIGNAL( toggled( bool ) ), this, + SLOT( reverseBtnToggled( bool ) ) ); + toolTip::add( m_reverseButton, tr( "Reverse sample" ) ); +#ifdef QT4 + m_reverseButton->setWhatsThis( +#else + QWhatsThis::add( m_reverseButton, +#endif + tr( "If you enable this button, the whole sample is reversed. " + "This is useful for cool effects, e.g. a reversed " + "crash." ) ); + + m_loopButton = new pixmapButton( this ); + m_loopButton->move( 180, 124 ); + m_loopButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_on" ) ); + m_loopButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "loop_off" ) ); + m_loopButton->setBgGraphic( getBackground( m_loopButton ) ); + toolTip::add( m_loopButton, + tr( "Loop sample at start- and end-point" ) ); +#ifdef QT4 + m_loopButton->setWhatsThis( +#else + QWhatsThis::add( m_loopButton, +#endif + tr( "Here you can set, whether Looping-Mode is enabled. If " + "enabled, AudioFileProcessor loops between start- and " + "end-point of a sample until the whole note is played. " + "This is useful for things like string- and choir-" + "samples." ) ); + + m_ampKnob = new knob( knobDark_28, this, tr( "Amplify" ) ); + m_ampKnob->setRange( 0, 500, 1.0f ); + m_ampKnob->move( 6, 114 ); + m_ampKnob->setValue( 100.0f, TRUE ); + m_ampKnob->setHintText( tr( "Amplify:" )+" ", "%" ); + m_ampKnob->setLabel( tr( "AMP" ) ); + connect( m_ampKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( ampKnobChanged( float ) ) ); +#ifdef QT4 + m_ampKnob->setWhatsThis( +#else + QWhatsThis::add( m_ampKnob, +#endif + tr( "With this knob you can set the amplify-ratio. When you " + "set a value of 100% your sample isn't changed. " + "Otherwise it will be amplified up or down (your " + "actual sample-file isn't touched!)" ) ); + + m_startKnob = new knob( knobDark_28, this, tr( "Start of sample" ) ); + m_startKnob->setRange( 0, 1.0, 0.00001 ); + m_startKnob->move( 46, 114 ); + m_startKnob->setValue( 0.0, TRUE ); + m_startKnob->setHintText( tr( "Startpoint:" )+" ", "" ); + m_startKnob->setLabel( tr( "START" ) ); + connect( m_startKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( startKnobChanged( float ) ) ); +#ifdef QT4 + m_startKnob->setWhatsThis( +#else + QWhatsThis::add( m_startKnob, +#endif + tr( "With this knob you can set the point where " + "AudioFileProcessor should begin playing your sample. " + "If you enable Looping-Mode, this is the point to " + "which AudioFileProcessor returns if a note is longer " + "than the sample between start- and end-point." ) ); + + m_endKnob = new knob( knobDark_28, this, tr( "End of sample" ) ); + m_endKnob->setRange( 0, 1.0, 0.00001 ); + m_endKnob->move( 84, 114 ); + m_endKnob->setValue( 1.0, TRUE ); + m_endKnob->setHintText( tr( "Endpoint:" )+" ", "" ); + m_endKnob->setLabel( tr( "END" ) ); + connect( m_endKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( endKnobChanged( float ) ) ); +#ifdef QT4 + m_endKnob->setWhatsThis( +#else + QWhatsThis::add( m_endKnob, +#endif + tr( "With this knob you can set the point where " + "AudioFileProcessor should stop playing your sample. " + "If you enable Looping-Mode, this is the point where " + "AudioFileProcessor returns if a note is longer than " + "the sample between start- and end-point." ) ); + + m_viewLinesPB = new pixmapButton( this ); + m_viewLinesPB->move( 154, 158 ); + m_viewLinesPB->setBgGraphic( getBackground( m_viewLinesPB ) ); + if( m_drawMethod == sampleBuffer::LINE_CONNECT ) + { +#ifdef QT4 + m_viewLinesPB->setChecked( TRUE ); +#else + m_viewLinesPB->setOn( TRUE ); +#endif + } + connect( m_viewLinesPB, SIGNAL( toggled( bool ) ), this, + SLOT( lineDrawBtnToggled( bool ) ) ); +#ifdef QT4 + m_viewLinesPB->setWhatsThis( +#else + QWhatsThis::add( m_viewLinesPB, +#endif + tr( "Activate this button, if your sample should be drawn " + "with connected lines. This doesn't change the " + "sound itself. It just gives you another view to your " + "sample." ) ); + + m_viewDotsPB = new pixmapButton( this ); + m_viewDotsPB->move( 204, 158 ); + m_viewDotsPB->setBgGraphic( getBackground( m_viewDotsPB ) ); + if( m_drawMethod == sampleBuffer::DOTS ) + { +#ifdef QT4 + m_viewDotsPB->setChecked( TRUE ); +#else + m_viewDotsPB->setOn( TRUE ); +#endif + } + connect( m_viewDotsPB, SIGNAL( toggled( bool ) ), this, + SLOT( dotDrawBtnToggled( bool ) ) ); +#ifdef QT4 + m_viewDotsPB->setWhatsThis( +#else + QWhatsThis::add( m_viewDotsPB, +#endif + tr( "Activate this button, if your sample should be drawn " + "with dots. This doesn't change the sound itself. " + "It just gives you another view to your sample." ) ); + + QButtonGroup * view_group = new QButtonGroup( this ); + view_group->addButton( m_viewLinesPB ); + view_group->addButton( m_viewDotsPB ); + view_group->setExclusive( TRUE ); +#ifndef QT4 + view_group->hide(); + + setBackgroundMode( Qt::NoBackground ); +#endif +} + + + + +audioFileProcessor::~audioFileProcessor() +{ +} + + + + +void audioFileProcessor::saveSettings( QDomDocument & _doc, + QDomElement & _parent ) +{ + QDomElement afp_de = _doc.createElement( nodeName() ); + afp_de.setAttribute( "src", m_sampleBuffer.audioFile() ); + afp_de.setAttribute( "sframe", QString::number( + m_sampleBuffer.startFrame() / + (float)m_sampleBuffer.frames() ) ); + afp_de.setAttribute( "eframe", QString::number( + m_sampleBuffer.endFrame() / + (float)m_sampleBuffer.frames() ) ); + afp_de.setAttribute( "reversed", QString::number( + m_reverseButton->isChecked() ) ); + afp_de.setAttribute( "looped", QString::number( + m_loopButton->isChecked() ) ); + afp_de.setAttribute( "amp", QString::number( m_ampKnob->value() ) ); + _parent.appendChild( afp_de ); +} + + + + +void audioFileProcessor::loadSettings( const QDomElement & _this ) +{ + setAudioFile( _this.attribute( "src" ) ); + setStartAndEndKnob( _this.attribute( "sframe" ).toFloat(), + _this.attribute( "eframe" ).toFloat() ); + m_reverseButton->setChecked( _this.attribute( "reversed" ).toInt() ); + m_loopButton->setChecked( _this.attribute( "looped" ).toInt() ); + m_ampKnob->setValue( _this.attribute( "amp" ).toFloat() ); +} + + + + +QString audioFileProcessor::nodeName( void ) const +{ + return( audiofileprocessor_plugin_descriptor.name ); +} + + + + +void audioFileProcessor::setParameter( const QString & _param, + const QString & _value ) +{ + if( _param == "audiofile" ) + { + setAudioFile( _value ); + } +} + + + + +Uint32 audioFileProcessor::getBeatLen( notePlayHandle * _n ) const +{ + const float freq_factor = BASE_FREQ / + ( getChannelTrack()->frequency( _n ) * + DEFAULT_SAMPLE_RATE / + mixer::inst()->sampleRate() ); + + return( static_cast( floorf( ( m_sampleBuffer.endFrame() - + m_sampleBuffer.startFrame() ) * + freq_factor ) ) ); +} + + + + +void audioFileProcessor::setAudioFile( const QString & _audio_file ) +{ + // is current channel-name equal to previous-filename?? + if( getChannelTrack()->name() == + QFileInfo( m_sampleBuffer.audioFile() ).fileName() || + m_sampleBuffer.audioFile() == "" ) + { + // then set it to new one + getChannelTrack()->setName( QFileInfo( _audio_file + ).fileName() ); + } + // else we don't touch the channel-name, because the user named it self + + m_sampleBuffer.setAudioFile( _audio_file ); + setStartAndEndKnob( 0.0f, 1.0f ); +} + + + + + +void audioFileProcessor::playNote( notePlayHandle * _n ) +{ + const Uint32 frames = mixer::inst()->framesPerAudioBuffer(); + sampleFrame * buf = bufferAllocator::alloc( frames ); + + // calculate frequency of note + const float note_freq = getChannelTrack()->frequency( _n ) / + ( mixer::inst()->sampleRate() / + DEFAULT_SAMPLE_RATE ); + + if( m_sampleBuffer.play( buf, _n->totalFramesPlayed(), + frames, note_freq, + m_loopButton->isChecked(), + &_n->m_pluginData ) == TRUE ) + { + getChannelTrack()->processAudioBuffer( buf, frames, _n ); + } + bufferAllocator::free( buf ); +} + + + + +void audioFileProcessor::deleteNotePluginData( notePlayHandle * _n ) +{ + if( _n->m_pluginData != NULL ) + { + m_sampleBuffer.deleteResamplingData( &_n->m_pluginData ); + } +} + + + + +void audioFileProcessor::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap pm( rect().size() ); + pm.fill( this, rect().topLeft() ); + + QPainter p( &pm, this ); +#endif + + p.drawPixmap( 0, 0, *s_artwork ); + + + QString file_name = ""; + Uint16 idx = m_sampleBuffer.audioFile().length(); + + p.setFont( pointSize<8>( p.font() ) ); + + QFontMetrics fm( font() ); + + // simple algorithm for creating a text from the filename that + // matches in the white rectangle +#ifdef QT4 + while( idx > 0 && + fm.size( Qt::TextSingleLine, file_name + "..." ).width() < 225 ) +#else + while( idx > 0 && + fm.size( Qt::SingleLine, file_name + "..." ).width() < 225 ) +#endif + { + file_name = m_sampleBuffer.audioFile()[--idx] + file_name; + } + + if( idx > 0 ) + { + file_name = "..." + file_name; + } + + p.setPen( QColor( 255, 255, 255 ) ); + p.drawText( 8, 84, file_name ); + + p.drawPixmap( 2, 172, m_graph ); + + + p.setPen( QColor( 0xFF, 0xAA, 0x00 ) ); + const QRect graph_rect( 4, 174, 241, 70 ); + const Uint32 frames = tMax( m_sampleBuffer.frames(), + static_cast( 1 ) ); + const Uint16 start_frame_x = m_sampleBuffer.startFrame() * + graph_rect.width() / frames; + const Uint16 end_frame_x = m_sampleBuffer.endFrame() * + ( graph_rect.width() - 1 ) / frames; + + p.drawLine( start_frame_x + graph_rect.x(), graph_rect.y(), + start_frame_x + graph_rect.x(), + graph_rect.height() + graph_rect.y() ); + p.drawLine( end_frame_x + graph_rect.x(), graph_rect.y(), + end_frame_x + graph_rect.x(), + graph_rect.height() + graph_rect.y() ); + +#ifndef QT4 + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void audioFileProcessor::sampleUpdated( void ) +{ + m_graph = QPixmap( 245, 75 ); + copyBlt( &m_graph, 0, 0, s_artwork, 2, 172, m_graph.width(), + m_graph.height() ); + QPainter p( &m_graph ); + m_sampleBuffer.drawWaves( p, QRect( 2, 2, m_graph.width() - 4, + m_graph.height() - 4 ), + m_drawMethod ); + update(); +} + + + + +void audioFileProcessor::reverseBtnToggled( bool _on ) +{ + m_sampleBuffer.setReversed( _on ); + songEditor::inst()->setModified(); +} + + + + +void audioFileProcessor::lineDrawBtnToggled( bool _on ) +{ + if( _on ) + { + m_drawMethod = sampleBuffer::LINE_CONNECT; + sampleUpdated(); + } +} + + + + +void audioFileProcessor::dotDrawBtnToggled( bool _on ) +{ + if( _on ) + { + m_drawMethod = sampleBuffer::DOTS; + sampleUpdated(); + } +} + + + + +void audioFileProcessor::ampKnobChanged( float _val ) +{ + m_sampleBuffer.setAmplification( _val/100.0f ); +} + + + + +void audioFileProcessor::setStartAndEndKnob( float _s, float _e ) +{ + // because the signal-handlers of valuechanges of start- and end-knob + // do range checking, depending on value of the other knob, we have to + // disconnect the signal-handlers, set then the values, connect again + // and then let the changes take effect... + m_startKnob->disconnect(); + m_endKnob->disconnect(); + m_startKnob->setValue( _s ); + m_endKnob->setValue( _e ); + connect( m_startKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( startKnobChanged( float ) ) ); + connect( m_endKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( endKnobChanged( float ) ) ); + m_startKnob->setValue( _s ); + m_endKnob->setValue( _e ); +} + + + + +void audioFileProcessor::startKnobChanged( float _new_value ) +{ + if( _new_value < m_endKnob->value() ) + { + m_sampleBuffer.setStartFrame( static_cast( _new_value * + m_sampleBuffer.frames() ) ); + } + else + { + m_startKnob->setValue( m_endKnob->value() - 0.01 ); + } + update(); +} + + + + +void audioFileProcessor::endKnobChanged( float _new_value ) +{ + if( _new_value > m_startKnob->value() ) + { + m_sampleBuffer.setEndFrame( static_cast( _new_value * + m_sampleBuffer.frames() ) - 1 ); + } + else + { + m_endKnob->setValue( m_startKnob->value() + 0.01 ); + } + update(); +} + + + + +void audioFileProcessor::openAudioFile( void ) +{ + QString af = m_sampleBuffer.openAudioFile(); + if( af != "" ) + { + setAudioFile( af ); + songEditor::inst()->setModified(); + } +} + + + + +extern "C" +{ + +// neccessary for getting instance out of shared lib +plugin * lmms_plugin_main( void * _data ) +{ + return( new audioFileProcessor( + static_cast( _data ) ) ); +} + + +} + + + +#undef isChecked +#undef isOn + +#include "audio_file_processor.moc" + diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h new file mode 100644 index 000000000..b236d7f2b --- /dev/null +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -0,0 +1,116 @@ +/* + * audio_file_processor.h - declaration of class audioFileProcessor + * (instrument-plugin for using audio-files) + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_FILE_PROCESSOR_H +#define _AUDIO_FILE_PROCESSOR_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#include "instrument.h" +#include "sample_buffer.h" +#include "spc_bg_hndl_widget.h" + + +class knob; +class pixmapButton; + + +class audioFileProcessor : public instrument, public specialBgHandlingWidget +{ + Q_OBJECT +public: + audioFileProcessor( channelTrack * _channel_track ); + virtual ~audioFileProcessor(); + + virtual void FASTCALL playNote( notePlayHandle * _n ); + virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + virtual void FASTCALL setParameter( const QString & _param, + const QString & _value ); + + virtual QString nodeName( void ) const; + + virtual Uint32 FASTCALL getBeatLen( notePlayHandle * _n ) const; + + +public slots: + void setAudioFile( const QString & _audio_file ); + + +protected slots: + void openAudioFile( void ); + void reverseBtnToggled( bool _on ); + void ampKnobChanged( float _new_value ); + void startKnobChanged( float _new_value ); + void endKnobChanged( float _new_value ); + void lineDrawBtnToggled( bool _on ); + void dotDrawBtnToggled( bool _on ); + void sampleUpdated( void ); + + +protected: + void paintEvent( QPaintEvent * ); + + +private: + static QPixmap * s_artwork; + + + sampleBuffer m_sampleBuffer; + + sampleBuffer::drawMethods m_drawMethod; + + QPixmap m_graph; + knob * m_ampKnob; + knob * m_startKnob; + knob * m_endKnob; + pixmapButton * m_openAudioFileButton; + pixmapButton * m_viewLinesPB; + pixmapButton * m_viewDotsPB; + pixmapButton * m_reverseButton; + pixmapButton * m_loopButton; + + + void updateSample( void ); + void FASTCALL setStartAndEndKnob( float _s, float _e ); + +} ; + + +#endif diff --git a/plugins/audio_file_processor/logo.png b/plugins/audio_file_processor/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8b2569c1fa5fd326f967e3a827e913cfc8f295 GIT binary patch literal 2249 zcmV;)2sZbLP)J9x_bkgY%6(uetWFqKfN**fIlDX_b{}|)-<RbMr?9d@O^Hq(VtCk(Fj6Cu=M>mtJGXFMmu!%e7Gf zKlB`lfqPVF=tGGJVKEw+ms`lJtUUBj_;}&9U0l4{j35Z30zQ&WL}VaY-4#WVMTK)2 zxTLUm^=9Oy4%Y20=iH^*s6xbRdvU8!vWBcE3MB=z@LUfN@cB8u{vdP8$MU5gex3bC zeu2m9jR+)>?+P8@QJ;krMPY2JjfvVE{?&c~t6=2K1^d|e>JuDjI>ht)cTjz~{vH)g zr2qSB0{}XWh6R(0`RGzL9;FWvh4N|7^Y)${IOcY+{=heR^Z2iEdfX!#tovdEgJHqE z{35P4cJi_B9LL?i$45W&Ohp_h`yLy%JjxqQ2YKPOom{)oo-pXR85oHW1(Wm8^%$vi z)v)KCw^?_zoWpJJ;l<0Qnd^9CXE{B^Ev)^)OMF;&eng&mSU)h5Fd!=}gQpjMmy7I&?Rvz19$O%1qdi zC96gP@W{-0T&QVg&dfY&?U%4S?;c^J&cK${o5*dSh$M!nZEc7;@(IsE$ucl_T|BiA z-<1HCbRAB;llrzMgs|r`$E5RQ!Q&{s(B^TE2Q<8#TCvgvZ) z#5zUKKO4?5B-#6vDS@>QKgs&ftYTTg!?*L}M1nbWEF+G`pdzth(ith9Jd3)9YI5?^ zxbQ(O{R3W12IF1NF2L{OqA zB8tMuLy7QkIvMao+vZO%!Q0_Sr&ZxHyQyz$gg8_*5eC%pFc1s8fIyeCo8}u=xZ&s~ zB!#dTEM(gzFhMg8yWP#0Ts@Uns`x_5im1Ats4s|`)W-sk0=;fK2TuH)N(>i?MglL%5~nYoq5SA}D5h=d3b#E(rgj)49s;|g{3&zOM2>*w!dB>vr6Lw@$Oq!O5f zgSp|r_x9P@^T&ORFE#Pq(r1`r$fFMjE_!g&OCP;Bq;7lxAAWxTwJejORgpH)%s-kx zA{YuK%s|2_6wMo-s69!iv5N|Yg~$xxruWwS$OM%E?12s%`_AK)Ja>&$&SWb!3bi;r zPSUNmgc*p-h+~D*k}UCI(^fCsHsMJO1gFBqh6pq|^)(lF~G3eer-!J;vvSL>3pfw=rY ztdUTQDrSz)r@EyEXPy;UvTwsYyk;BT6an)C)X4_sghzG*mWz zg3liy7#zL~1Oha4c{qKlmDyrG>r2+6(P)zXeIJv7=-_YN=cP|!&$8|8`s*v4a``Ek zVji(djqzr>}hee^cDSe!baZ!i58IaxU)%J!0uM#Jw{&RU5@mqNv<{hV&T zf;nA}!K_2A7V!H+^t=2FIQ&f0OyaqsXIV9S73rzzasBK~5O=oC#wi5^iFsKSlvQ1)6(L)qZEGEsGhDxOxl~tRV3?veT0;CvHSX!`z z$7U=h7z!dwGJ+_e5><$zIBM%=NjtJ=9(icJAP8tRS`_vDUG$3;N+>>`uix!1#tJP|wf)xZIV!4%J-6<-S z3YAKA*R@KeLakOuEE_*|u$)+}YV#sVK@3z%}e%2~iZyq9|_9&(D9+X0w?e zgslc`ZEeBU*48>jQ7VA+9$;?Ws1x9rWlk~7tT_T}DM0D7p{Q(6LNC^>Sbqb{Q|+nzufN^&|2iq%nnxL}oj0yuD39oc z!U})@h}ev1XVmF4NyE?zSAW7*Wgibe zWv`l7w{0~?@0-2@v%iz~)T49-#8~lgi>6lP(w(KLh^wBfcDH<>R7#MlIh5I=Ya4@W z8ix33gobC>UpudITVvbn=J7-8M`e}8om~b;CT?Zlt4m+~;b6DF%^ez;pHp8{QBCLq zPVA9WSFW9*omYBh%_{YVz4ffiE(|o{bKQkHnAB!Avdl*&>h%iuv!!+OuropQwYEqv>gNF4N=f rwFjJjM@TeVCLHR2F|gRTw|V~qaY3pCJB6|e00000NkvXXu0mjfK>_+e literal 0 HcmV?d00001 diff --git a/plugins/audio_file_processor/loop_on.png b/plugins/audio_file_processor/loop_on.png new file mode 100644 index 0000000000000000000000000000000000000000..bdb0c47f2d5d18af10bf5910a218497c075adeb8 GIT binary patch literal 933 zcmV;W16urvP)`_Iq&{rc??X@U(^1l;>mj(N4<9@h;i+YsP-q+hDTURQ!sB{ z(_CL*zJZ}g2glCfMDof?8dk63@iiqCpMFx?`JsBE*1@xvug7cwz&5HE1j>)Mtb2ZW zq+%YnH-$qM5I9nzV-VNxGUdKajkbY-u4&poKKibiP4CgzPP8gm{pA}iuZ6G8>7m!@ zC&+ws=>jAOfshKrlQClEb@Gd&gab9WFpAA=%Fy!lg7&uct>KQ@W9XwQ>&@rr?Yojl zCda2##if{LR!040MXNS$(MyaN$?iEE=-BsFDjna&h3Q#sNpNw4Kah*_TMnC)X5Kp9 z{=@M2&^8Qlo4E8agD(=Q=&D?_zr64KSnBhhV~3>7zQ={>82}O5T{2l0sdKv(j~pBs zQZ5emyiS*OgjQdW4C5qx0go&9oM9=6cxqy=6wXyB{_2kx6)W$zWhYpq7_QUH2J2lZ}#v1Z)E%CgU|fjdFcze zWJlFaYKgLf0JZq3q;&4uN6vR&J}tZCVfmB%UT%5ynz8IpfxI6x)=x&+W^tU$;6qP}I#-BF5~EFuhj>E()4ab=7n9P)qwxwR)1% zW2XoyWrV#^9AN_>5Lk-IUFQ~F-OZDkt-SyCnaOYadKxsO>NvJYCKE|q37fHa9KSY8 z+Oja63;+^|ipD}~3fUn)2Tyj!`hV=-$>8v%`%P>%6eX>`)AmH$p2*rDA#aFt9hc34 zk-HO$Hs za_RGt^`#HzIU;moBYtU7rfGccl4bMh==IP{vc)fwz0j^I-Th|nfZ5XO;d}S-3dcou UoR>5U4*&oF07*qoM6N<$f`x|7kpKVy literal 0 HcmV?d00001 diff --git a/plugins/audio_file_processor/reverse_on.png b/plugins/audio_file_processor/reverse_on.png new file mode 100644 index 0000000000000000000000000000000000000000..42774aded3d195f23acc5177591e9c8d01ffcf71 GIT binary patch literal 755 zcmV5sO=U-bpF_>G$8)xleZw@;^2I^vZB)_((9~ zuUD&Al$Dr1BQQAh-1kpl)%h4jci)Ef6U3FKOS@TIY`^VhntT+(!@w*e=)wP`LsKErjbhjt4wbB9z*b zs44Gi-n=SWUQXz_T8@x+{ zoR-khke86ndjzg_T|)!{*AiS_{MQX@J+=Z(Zoj z^bY^-fr*0hXxJj8?wPSMK{3Qjq(ObGh$cJq6^>3%Sov$v;uA9dWnaO=gu z!xYhAkwhFyA`l6B1D3OWFgw8C@A&n8n{@Y;@sY8nzDs>4ZY1wyMpDC+&C`^Zg3kmM zvZPX`6hb3re1`PFufVBc_uS3PuSUApY^?5R+f_d+6qHK!zx?n?Na@#7=KuMP=>sVW losaEg&)d}KNoARfegaVs?gm-)e98a-002ovPDHLkV1mUjVut_# literal 0 HcmV?d00001 diff --git a/plugins/plucked_string_synth/Makefile.am b/plugins/plucked_string_synth/Makefile.am new file mode 100644 index 000000000..40e103b7b --- /dev/null +++ b/plugins/plucked_string_synth/Makefile.am @@ -0,0 +1,33 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I. + + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="pluckedstringsynth" + + +%.moc: ./%.h + $(MOC) -o $@ $< + + +MOC_FILES = ./plucked_string_synth.moc + +BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h +EMBEDDED_RESOURCES = $(wildcard *png) + +./embedded_resources.h: $(EMBEDDED_RESOURCES) + $(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@ + +EXTRA_DIST = $(EMBEDDED_RESOURCES) + + +CLEANFILES = $(MOC_FILES) ./embedded_resources.h + + + +pkglib_LTLIBRARIES= libpluckedstringsynth.la + +libpluckedstringsynth_la_SOURCES = plucked_string_synth.cpp plucked_string_synth.h + +$(libpluckedstringsynth_la_SOURCES): ./embedded_resources.h diff --git a/plugins/plucked_string_synth/artwork.png b/plugins/plucked_string_synth/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..d972d07448aeeb56994b24657db1db649cdf6640 GIT binary patch literal 40827 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 XqVaC|zoD}+00000NkvXXu0mjfuLuJ1 literal 0 HcmV?d00001 diff --git a/plugins/plucked_string_synth/logo.png b/plugins/plucked_string_synth/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2582b463cdab32f0e515edf603aee31de9df4c86 GIT binary patch literal 3444 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 WykTSD)=ZoL0000 + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "plucked_string_synth.h" +#include "channel_track.h" +#include "note_play_handle.h" +#include "templates.h" +#include "buffer_allocator.h" +#include "knob.h" + +#include "embed.cpp" + + +extern "C" +{ + +plugin::descriptor pluckedstringsynth_plugin_descriptor = +{ + STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), + "PluckedStringSynth", + QT_TRANSLATE_NOOP( "plugin", + "cheap synthesis of guitar/harp-like sounds" ), + "Tobias Doerffel ", + 0x0100, + plugin::INSTRUMENT, + PLUGIN_NAME::findEmbeddedData( "logo.png" ) +} ; + +} + + +// TODO: make this synth stereo for better better spacial (room) feeling and +// add distortion + +pluckedStringSynth::pluckedStringSynth( channelTrack * _channel_track ) : + instrument( _channel_track, + pluckedstringsynth_plugin_descriptor.public_name ) +{ + m_pickKnob = new knob( knobDark_28, this, tr( "Pick position" ) ); + m_pickKnob->setRange( 0.0f, 0.5f, 0.005f ); + m_pickKnob->setValue( 0.0f, TRUE ); + m_pickKnob->move( 86, 134 ); + m_pickKnob->setHintText( tr( "Pick position:" ) + " ", "" ); + + m_pickupKnob = new knob( knobDark_28, this, tr( "Pickup position" ) ); + m_pickupKnob->setRange( 0.0f, 0.5f, 0.005f ); + m_pickupKnob->setValue( 0.05f, TRUE ); + m_pickupKnob->move( 138, 134 ); + m_pickupKnob->setHintText( tr( "Pickup position:" ) + " ", "" ); +#ifdef QT4 + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( + "artwork" ) ); + setPalette( pal ); +#else + setErasePixmap( PLUGIN_NAME::getIconPixmap( "artwork" ) ); +#endif +} + + + + +pluckedStringSynth::~pluckedStringSynth() +{ +} + + + + +void pluckedStringSynth::saveSettings( QDomDocument & _doc, + QDomElement & _parent ) +{ + QDomElement pss_de = _doc.createElement( nodeName() ); + pss_de.setAttribute( "pick", QString::number( m_pickKnob->value() ) ); + pss_de.setAttribute( "pickup", QString::number( + m_pickupKnob->value() ) ); + _parent.appendChild( pss_de ); +} + + + + +void pluckedStringSynth::loadSettings( const QDomElement & _this ) +{ + m_pickKnob->setValue( _this.attribute( "pick" ).toFloat() ); + m_pickupKnob->setValue( _this.attribute( "pickup" ).toFloat() ); +} + + + + +QString pluckedStringSynth::nodeName( void ) const +{ + return( pluckedstringsynth_plugin_descriptor.name ); +} + + + + +void pluckedStringSynth::playNote( notePlayHandle * _n ) +{ + if ( _n->totalFramesPlayed() == 0 ) + { + float freq = getChannelTrack()->frequency( _n ); + _n->m_pluginData = new pluckSynth( freq, m_pickKnob->value(), + m_pickupKnob->value() ); + } + + const Uint32 frames = mixer::inst()->framesPerAudioBuffer(); + sampleFrame * buf = bufferAllocator::alloc( frames ); + + pluckSynth * ps = static_cast( _n->m_pluginData ); + for( Uint32 frame = 0; frame < frames; ++frame ) + { + const sampleType cur = ps->nextStringSample(); + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + buf[frame][chnl] = cur; + } + } + + getChannelTrack()->processAudioBuffer( buf, frames, _n ); + + bufferAllocator::free( buf ); +} + + + + +void pluckedStringSynth::deleteNotePluginData( notePlayHandle * _n ) +{ + delete static_cast( _n->m_pluginData ); +} + + + + + +pluckSynth::delayLine * FASTCALL pluckSynth::initDelayLine( int _len ) +{ + delayLine * dl = new pluckSynth::delayLine[_len]; + dl->length = _len; + if( _len > 0 ) + { + dl->data = new sampleType[_len]; + } + else + { + dl->data = NULL; + } + + dl->pointer = dl->data; + dl->end = dl->data + _len - 1; + + return( dl ); +} + + + + +void FASTCALL pluckSynth::freeDelayLine( delayLine * _dl ) +{ + if( _dl && _dl->data ) + { + delete[] _dl->data; + } + + _dl->data = NULL; + delete[] _dl; +} + + + + +pluckSynth::pluckSynth( float _pitch, float _pick, float _pickup ) +{ + const float AMP = 1.5f; + int rail_length = static_cast( mixer::inst()->sampleRate() / 2 / + _pitch ) + 1; + // Round pick position to nearest spatial sample. + // A pick position at x = 0 is not allowed. + int pick_sample = static_cast( tMax( rail_length * _pick, + 1.0f ) ); + float initial_shape[rail_length]; + + m_upperRail = pluckSynth::initDelayLine( rail_length ); + m_lowerRail = pluckSynth::initDelayLine( rail_length ); + +//#define METALLIC_PLUCK +#ifdef METALLIC_PLUCK + for ( int i = 0; i < rail_length; i++ ) + { + initial_shape[i] = rand() * AMP / RAND_MAX; + } + + initial_shape[pick_sample] = 0.5; + initial_shape[pick_sample+1] = 0.5; + +#else + float upslope = AMP / pick_sample; + const float downslope = AMP / ( rail_length - pick_sample - 1 ); + + for( int i = 0; i < pick_sample; i++ ) + { + initial_shape[i] = upslope * i; + } + + for( int i = pick_sample; i < rail_length; i++ ) + { + initial_shape[i] = downslope * ( rail_length - 1 - i ); + } +#endif + + // Initial conditions for the ideal plucked string. + // "Past history" is measured backward from the end of the array. + pluckSynth::setDelayLine( m_lowerRail, initial_shape, 0.5f ); + pluckSynth::setDelayLine( m_upperRail, initial_shape, 0.5f ); + + m_pickupLoc = static_cast( _pickup * rail_length ); +} + + + + +extern "C" +{ + +// neccessary for getting instance out of shared lib +plugin * lmms_plugin_main( void * _data ) +{ + return( new pluckedStringSynth( + static_cast( _data ) ) ); +} + + +} + + diff --git a/plugins/plucked_string_synth/plucked_string_synth.h b/plugins/plucked_string_synth/plucked_string_synth.h new file mode 100644 index 000000000..b066f8903 --- /dev/null +++ b/plugins/plucked_string_synth/plucked_string_synth.h @@ -0,0 +1,216 @@ +/* + * plucked_string_sytn.h - declaration of class pluckedStringSynth which + * is a synth for plucked string-sounds + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PLUCKED_STRING_SYNTH_H +#define _PLUCKED_STRING_SYNTH_H + +#include "instrument.h" + + +class knob; +class notePlayHandle; + + +// the actual synth +class pluckSynth +{ +public: + pluckSynth( float _pitch, float _pick, float _pickup ); + + inline ~pluckSynth( void ) + { + pluckSynth::freeDelayLine( m_upperRail ); + pluckSynth::freeDelayLine( m_lowerRail ); + } + + inline sampleType nextStringSample( void ) + { + // Output at pickup position + sampleType outsamp = rgDlAccess( m_upperRail, m_pickupLoc ); + outsamp += lgDlAccess( m_lowerRail, m_pickupLoc ); + + // Sample traveling into "bridge" + sampleType ym0 = lgDlAccess( m_lowerRail, 1 ); + // Sample to "nut" + sampleType ypM = rgDlAccess( m_upperRail, + m_upperRail->length - 2 ); + + // String state update + + // Decrement pointer and then update + rgDlUpdate( m_upperRail, -bridgeReflection( ym0 ) ); + // Update and then increment pointer + lgDlUpdate( m_lowerRail, -ypM ); + + return( outsamp ); + } + + +private: + struct delayLine + { + sampleType * data; + int length; + sampleType * pointer; + sampleType * end; + } ; + + delayLine * m_upperRail; + delayLine * m_lowerRail; + int m_pickupLoc; + + static delayLine * FASTCALL initDelayLine( int _len ); + static void FASTCALL freeDelayLine( delayLine * _dl ); + static inline void setDelayLine( delayLine * _dl, float * _values, + float _scale ) + { + for( int i = 0; i < _dl->length; ++i ) + { + _dl->data[i] = _scale * _values[i]; + } + } + + /* lgDlUpdate(dl, insamp); + * Places "nut-reflected" sample from upper delay-line into + * current lower delay-line pointer position (which represents + * x = 0 position). The pointer is then incremented (i.e. the + * wave travels one sample to the left), turning the previous + * position into an "effective" x = L position for the next + * iteration. */ + static inline void lgDlUpdate( delayLine * _dl, sampleType _insamp ) + { + register sampleType * ptr = _dl->pointer; + *ptr = _insamp; + ++ptr; + if( ptr > _dl->end ) + { + ptr = _dl->data; + } + _dl->pointer = ptr; + } + + /* rgDlUpdate(dl, insamp); + * Decrements current upper delay-line pointer position (i.e. + * the wave travels one sample to the right), moving it to the + * "effective" x = 0 position for the next iteration. The + * "bridge-reflected" sample from lower delay-line is then placed + * into this position. */ + static inline void rgDlUpdate( delayLine * _dl, sampleType _insamp ) + { + register sampleType * ptr = _dl->pointer; + --ptr; + if( ptr < _dl->data ) + { + ptr = _dl->end; + } + *ptr = _insamp; + _dl->pointer = ptr; + } + + /* dlAccess(dl, position); + * Returns sample "position" samples into delay-line's past. + * Position "0" points to the most recently inserted sample. */ + static inline sampleType dlAccess( delayLine * _dl, int _position ) + { + sampleType * outpos = _dl->pointer + _position; + while( outpos < _dl->data ) + { + outpos += _dl->length; + } + while( outpos > _dl->end ) + { + outpos -= _dl->length; + } + return( *outpos ); + } + + /* + * Right-going delay line: + * -->---->---->--- + * x=0 + * (pointer) + * Left-going delay line: + * --<----<----<--- + * x=0 + * (pointer) + */ + + /* rgDlAccess(dl, position); + * Returns spatial sample at position "position", where position zero + * is equal to the current upper delay-line pointer position (x = 0). + * In a right-going delay-line, position increases to the right, and + * delay increases to the right => left = past and right = future. */ + static inline sampleType rgDlAccess( delayLine * _dl, int _position ) + { + return( dlAccess( _dl, _position ) ); + } + + /* lgDlAccess(dl, position); + * Returns spatial sample at position "position", where position zero + * is equal to the current lower delay-line pointer position (x = 0). + * In a left-going delay-line, position increases to the right, and + * delay DEcreases to the right => left = future and right = past. */ + static inline sampleType lgDlAccess( delayLine * _dl, int _position ) + { + return( dlAccess( _dl, _position ) ); + } + + static inline sampleType bridgeReflection( sampleType _insamp ) + { + static sampleType state = 0.0f; // filter memory + // Implement a one-pole lowpass with feedback coefficient = 0.5 + return( state = state*0.5f + _insamp*0.5f ); + } + +} ; + + + + +class pluckedStringSynth : public instrument +{ +public: + pluckedStringSynth(channelTrack * _channel_track ); + virtual ~pluckedStringSynth(); + + virtual void FASTCALL playNote( notePlayHandle * _n ); + virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + virtual QString nodeName( void ) const; + + +private: + knob * m_pickKnob; + knob * m_pickupKnob; + +} ; + + +#endif diff --git a/plugins/triple_oscillator/Makefile.am b/plugins/triple_oscillator/Makefile.am new file mode 100644 index 000000000..fd40793ad --- /dev/null +++ b/plugins/triple_oscillator/Makefile.am @@ -0,0 +1,33 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I. + + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="tripleoscillator" + + +%.moc: ./%.h + $(MOC) -o $@ $< + + +MOC_FILES = ./triple_oscillator.moc + +BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h +EMBEDDED_RESOURCES = $(wildcard *png) + +./embedded_resources.h: $(EMBEDDED_RESOURCES) + $(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@ + +EXTRA_DIST = $(EMBEDDED_RESOURCES) + + +CLEANFILES = $(MOC_FILES) ./embedded_resources.h + + + +pkglib_LTLIBRARIES= libtripleoscillator.la + +libtripleoscillator_la_SOURCES = triple_oscillator.cpp triple_oscillator.h + +$(libtripleoscillator_la_SOURCES): ./embedded_resources.h diff --git a/plugins/triple_oscillator/am_active.png b/plugins/triple_oscillator/am_active.png new file mode 100644 index 0000000000000000000000000000000000000000..88ea2abc5765342d1380726c095b41b13e67161b GIT binary patch literal 756 zcmV_C zX>@2HRA^-&M@dak?_?!z0007ONkltONCU##M&`SJ0R3%VreC?LsJPWGO^=`M73il{&^XU zqtK+NQo=J`J?Fgl``+`ObKih(LCh|7Dl~~1I<7*p%rQ-d01+6{2*c;OTqO+aL9jPA zkEsNw`~8hZHaE9^d*jr2e^K#raryc(Zhv=s@~_E}dH<{Xn14FIxU`7JHy*Eiw1PWd-`R&D zoem3yx3{-TjYh=9h)qgyTyMAEsn>_EkVY!KUkc(N4&rBLpXGne9}K-%cyaE>b672` zj+|<>`pPG^+s)x{gNWIPKdJTmtS-u-;T}>)oxX<*8<&xprx7;)jX-P--L9 zpXe9Q7ojJ$o$AbV4)N^n?)CdEs#GExw0gZxtyW7WPX&igueI0`h?nAIE!q0KwU%0o z{up(cL2HoyIbFS4MS3aSx!l3}`uf4;aU6HM-CnOtnwT;7Dd>Szvw|;y?+8I73;L+H*$u|cQR{3T8@L&4pg zr{GJjG0)0@oG;#JFh~$aWXaGzdcbcH^F3Q2$u(~No97C7r^yD~ED<~GF*rHu=*-g0@(CZ3jR?qbkyDhY(muKWb%rx6 mFi(yk8V4r1#O(ilAN>k6>E5=5?I%|N0000T0013yMObu0Z*6U5Zgc=ca%Ew3Wn>_C zX>@2HRA^-&M@dak?_?!z0007*Nklft%mKQ%@!)CLTO{Y*Ibc#FHlzL-<$9!O+wqg0k{w7uoHBLWq*aL^`LJ zH#6@y^WMxn0iOY|pao?pfd&?syjE=BhDDfzDG-1RfC+WG{rmjUMiN9ByuIe)9ReLWuG4xW2wQIQWuEr3xbm(=s$6t0+HKSK}_1 zQ`g(i{bpxp7Zw(PTrQ{UdT%oli2&7VwNxqri;IghGc&;D<>j!i%jH~KigWM;8g%(5&YM5oiqX0yXQkH=Fe6oe4LV6eA8Fc@TccXw-=zo83$ z8GgdGVd%wTu~Ml#hq>Kuuh*MSr5aRLiQPVUWrb{@$qpm7*thN)3n|N0*yxF{QNu=3IV65 zr=d_tQIt$3Gwg1fW~nrBYEUjmBH+{+6b86?~g7KZK{qcswqI*x%nzrBcAg#zr(6U0z^@apC8Qd@h&5;8| zRaMKf4i67&wHjcz+oREFB9R#8y}b9qEg%WsAP3_bDX51>S)Ngxpt5 fLj?T)`#<^v1{w^8vfjA000000NkvXXu0mjf0RLlS literal 0 HcmV?d00001 diff --git a/plugins/triple_oscillator/artwork.png b/plugins/triple_oscillator/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..13fbfe9f11b8f1c16ec2b8c5d273b4b9dfc23775 GIT binary patch literal 24224 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 literal 0 HcmV?d00001 diff --git a/plugins/triple_oscillator/btn_mask.png b/plugins/triple_oscillator/btn_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..bd257ea55009ef9e9ef6895942b23adc0a24b335 GIT binary patch literal 187 zcmeAS@N?&q;$mQ6;PUiv2?ElpKrFz>3?x@SSBV2sYymzYu0Z<#|Nl#G&c6#}aTa() z7Bet#3xhBt!>lbCYGe8 zD3oWGWGIAWq$;?3`UbpDQOyG?RPc0h45_%4TyfyQfrNtv3?5zvCJdf@Y+ejUSUNHj aCNq9%VEMRYK|(Z8ErX}4pUXO@geCwOjx_-Q literal 0 HcmV?d00001 diff --git a/plugins/triple_oscillator/fm_active.png b/plugins/triple_oscillator/fm_active.png new file mode 100644 index 0000000000000000000000000000000000000000..ea92fbfd8be19704827334b418fc133ac439545c GIT binary patch literal 694 zcmV;n0!jUeP)_C zX>@2HRA^-&M@dak?_?!z0006lNklHV%7{-6Ludy2ucYvr01gR3DYD|kN z!PpKgEQpc6pz6Y3fW(ZLnLDtsGj(EuX6r%+lBntE#`%z1>LzXy-`Na~ql%_{sOhuZ ztLN_C=iYmtvo-htKn&;51{cmChKTFJfE*Md4_VLv0YC)4VI0`DYZz@!>!;*3p~I|{ zE#JSd)DqaB3y8rtj332fV}1RhZLeq=(}Q+;+AEh2Tz9C{3d{)zpq9;69Or>)>QaX3 z(l{Bm-)>(_u=##dkMx9G{=U4ivjObB*sZPAfUS317sUX}G92gj$w}4s1*{6_KsAor zL2#$tjxH*VVg0n6&rTAz8m;mCVd-%3(;`rJ>Ivg{-sKX5;4F$-AYehj5j3Ujbvm7H zw|iL(pojXVMj8E4MzdwgTe7lL0SGZq%)$I%k{;*heJOk3fh)j=UKkFV&1NPuqfP2w z|6YIp<9-qs)(a>A)l=42%X{Vl){ZrJIsh7t#&3J$I35m%VK{^U&Jw?%lsY~>o?IN@ z!kba(9eK$rDW%+&d;thwhpb(7pW|Lepaevz_2VmD7C?CtUx&Rk_zOnK1sI^GM~MI1 z@UQ;$VSo;X4mrrCk~aqmW+5k_2nFz=Ke@!kN!u@z&;KlD!d)opkcSe?LuUL)r(IbF z=Ai_6171Q71azTKV)p5%COxxY0iM94_C zX>@2HRA^-&M@dak?_?!z0006)Nklx z{m;GkKj#Yg0DuP;G{J-}c;GUv1t0=RScU}B$MfHZoW?>VnT@7@o|3M+TZ_pe0(Bh3Kmu9Lsi$mt*yNZhqb}L z9!W9`py0vQ!rP;uSMo11p6 zRvSN=VHlQW9UdO~Zllq-FCrR^)@n5&L@Jf?qt$A4{FK+%m#+I099U7|JDf{7sMqTk z7Z($+vDfPr3I!kc`+Xl=U0rFKHa9mXgt)uA>vp@ku215l)9Ffi3%|frp#weJmZztu znl`T}%6LL77Tez5_VLEXhT}MX5yfILolXP!d_J8{>$+a4R7P@L*R?Fmwk>d=>+1z6 zWuwt}Oe1G!XFEGPJ}#Hbp63CB!C-H1FPF;!*=%-qcNZAvd!QW^92MMQt34`6%d(2a z;`n;KUOzZE@WJ1-`e$<$0-y{_`0pOr+S>Z(-cTT*Km-8bu!e{sbvl=TC z$>!k&yis5oRv`hQ`=KyvvJfO-6_x|=4kDn!E!g19j@5s&d02th@CqU-2#7)kf>41U x&>s%*DNTU|cm}KR5~3i~VPFvqSb6II=r_9=?kb3WzJ&k)002ovPDHLkV1l@SFt-2z literal 0 HcmV?d00001 diff --git a/plugins/triple_oscillator/logo.png b/plugins/triple_oscillator/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0b1d27664132f3dee925774a9e2908e297292d2a GIT binary patch literal 3536 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 literal 0 HcmV?d00001 diff --git a/plugins/triple_oscillator/mix_inactive.png b/plugins/triple_oscillator/mix_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..6f085b5266c2a65a5957bf16759fe8be1e659593 GIT binary patch literal 802 zcmV+-1Ks?IP)_C zX>@2HRA^-&M@dak?_?!z0007-Nkle7;pCxLPyGTMZPJ)n4#vcj8>2!g-FToW1w{mbWh-kBTL@{; zCe{8=FK=GvH}huRJOS?jxPcawpa6Ba0gL;}0SW{`1rG>72Ec-v)A?m=tn6}CgfO}h z+*1PFrdcbM#!T}m_&}2325L^{Ni^DATznGFP00HF zUuS3MyW>5hJoWqEyly;`mA?Cc~GiS6y}-QC?m`AOiAKmkt| z`7c^tUY?qoN+y#V8ylymr)z6#ilPjB`=AJT3!mUKJk(ejSiE6)25|{gn1%qjdPCu% zVJ-;3G^h@E3dnE;26P^dYS(NS#^EKr07V7?K8S)7GVmRm-64KYN#KDeFbdDX2SV-| gMj!^`|NB4s1>OV?G4f>`J^%m!07*qoM6N<$g3$J6egFUf literal 0 HcmV?d00001 diff --git a/plugins/triple_oscillator/sync_active.png b/plugins/triple_oscillator/sync_active.png new file mode 100644 index 0000000000000000000000000000000000000000..652661ea67afc0d495e13ba24037e5bde313a3d5 GIT binary patch literal 851 zcmV-Z1FZasP)_C zX>@2HRA^-&M@dak?_?!z0008YNkl}Qw^0V2?*5{B2Z*_ALX z1i@Cf_<%^!pCs#*%7Iqv5Lr?Y9V%gXW&i$Prl%jzW{(DehlikCt}iZLTUps|wT`kY zBIf%{=DUfB$NKtGYeV_P7Z*21ZlzkO!P4N=S5y0b+_&RqNTV-ZEJ zb~=SP9x4>tm)^J-566ek9qz65mJXKI8|$fj>H#IjakbrECt@UGm9-?P7mLMOtyUkd zhxITzA4SDz$Pn`KH&5RzohThYbv&v>%d^XcnZl7bkEB=Ax%ph*THnAA1Er@*J-2&C zKN!v2$g~Dp%dan&o-DO{+q-kSN6(Bl8e2(HC(laA0d90UTS=0{aXlGNwhwI=UMdu) zisf{uwNtg$Bdy@up!b{J#?y^zrkczr zt8cAVo~fiSrU%XrBwI=IQnOqx$8p^0U@u-^kl;7FrJ3b3uCv6Mt}6?iXObme>lT;D z5fHJ&d2odsslU#0fag=RyV37Ji%)okXTe05|0!dfCgI$D_iWH1>E{0*(Yd!GVV4-d zBJXmNSHblz{|g#Sv%u!R=3{|9e8@@W?$?(hj2I+Cd{^by%rHxn3%uXuSNM{bng6dm zzDL4y%y8lUe*Fw$j!F7|AK~BP1fP?mXJ?Dp&m?mZ!;DiR&o;q5um6Cv+~ytb?4VgM zdl_dqBEuMW$gxKAPZPe2gFBNI(8pe$V2n&eK$c_laFsmq!+WnYJjys@WC^0~!7h$7 d@_+wFzXB_08EXo7_C zX>@2HRA^-&M@dak?_?!z0009#NklA8BXq`FBd%yRdd-$I3xdQwOfB`oUgHy05bqOTuIWO zm6aDoMF~O3-M`?O5WuRcSFu>7s{R1wAezB|D@pq3bY?$%_^GPul^_UTj*n>cVt#(@ z;NYXG{s=G3kcB^9zC3>WwxhJvDvCmNb+y%MH4H;l)kq|AeSQ5L&eBrryLXMJr@zHw zA~?-(0iSgJGM!Ew9UZyd?=2R~-rk<7sy3S~7K@#noIF!yDs`#r=b*uBGyDZnRn3RP z;dngm_5Pg8C$rhC+wE>`ZJnN;j>qF8BO{4KA`*!>9F9yT zv$(i;c6Mg5Sln)RU0t0dNr#7rvMgU+WmNSVZsF7f-@~zCV`0 z+wGdBg+ifdG2kS_kB>v4kk99fL?ZL^^YM7x>-Ac#*8TncgM)*nrY1oUmY0`% zdU{$~T0)_a-|xSCo=x9OW z($bPF%TA|LQIx*EzQUrSD1ksg5CoISq$r9k%fR;bb|4TCMe&JuzJU%A^!rgW0A1G? z78V8u2D-buf%^LTLb)ugvDSabjXZ(=?aM)zQ%r3mk2~ZABkl+)XLH0hxp9v94;9F<_ n2b6 + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#define setChecked setOn + +#endif + + +#include "triple_oscillator.h" +#include "song_editor.h" +#include "channel_track.h" +#include "note_play_handle.h" +#include "knob.h" +#include "pixmap_button.h" +#include "buffer_allocator.h" +#include "debug.h" +#include "tooltip.h" + +#include "embed.cpp" + + +extern "C" +{ + +plugin::descriptor tripleoscillator_plugin_descriptor = +{ + STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), + "TripleOscillator", + QT_TRANSLATE_NOOP( "plugin", + "three powerful oscillators you can modulate " + "in several ways" ), + "Tobias Doerffel ", + 0x0100, + plugin::INSTRUMENT, + PLUGIN_NAME::findEmbeddedData( "logo.png" ) +} ; + +} + + +tripleOscillator::tripleOscillator( channelTrack * _channel_track ) : + instrument( _channel_track, + tripleoscillator_plugin_descriptor.public_name ), + m_modulationAlgo1( oscillator::MIX ), + m_modulationAlgo2( oscillator::MIX ) +{ +#ifdef QT4 + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); + setPalette( pal ); +#else + setErasePixmap( PLUGIN_NAME::getIconPixmap( "artwork" ) ); +#endif + + m_fm1OscBtn = new pixmapButton( this ); + m_fm1OscBtn->move( 80, 50 ); + m_fm1OscBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "fm_active" ) ); + m_fm1OscBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "fm_inactive" ) ); + m_fm1OscBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). + createHeuristicMask() ) ); + connect( m_fm1OscBtn, SIGNAL( toggled( bool ) ), this, + SLOT( fm1BtnToggled( bool ) ) ); + toolTip::add( m_fm1OscBtn, tr( "use frequency modulation for " + "modulating oscillator 2 with " + "oscillator 1" ) ); + + m_am1OscBtn = new pixmapButton( this ); + m_am1OscBtn->move( 120, 50 ); + m_am1OscBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "am_active" ) ); + m_am1OscBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "am_inactive" ) ); + m_am1OscBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). + createHeuristicMask() ) ); + connect( m_am1OscBtn, SIGNAL( toggled( bool ) ), this, + SLOT( am1BtnToggled( bool ) ) ); + toolTip::add( m_am1OscBtn, tr( "use amplitude modulation for " + "modulating oscillator 2 with " + "oscillator 1" ) ); + + m_mix1OscBtn = new pixmapButton( this ); + m_mix1OscBtn->move( 160, 50 ); + m_mix1OscBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "mix_active" ) ); + m_mix1OscBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "mix_inactive" ) ); + m_mix1OscBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). + createHeuristicMask() ) ); + connect( m_mix1OscBtn, SIGNAL( toggled( bool ) ), this, + SLOT( mix1BtnToggled( bool ) ) ); + toolTip::add( m_mix1OscBtn, tr( "mix output of oscillator 1 & 2" ) ); + + m_sync1OscBtn = new pixmapButton( this ); + m_sync1OscBtn->move( 200, 50 ); + m_sync1OscBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "sync_active" ) ); + m_sync1OscBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "sync_inactive" ) ); + m_sync1OscBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). + createHeuristicMask() ) ); + connect( m_sync1OscBtn, SIGNAL( toggled( bool ) ), this, SLOT( + sync1BtnToggled( bool ) ) ); + toolTip::add( m_sync1OscBtn, tr( "synchronize oscillator 1 with " + "oscillator 2" ) ); + + if( m_modulationAlgo1 == oscillator::FREQ_MODULATION ) + { + m_fm1OscBtn->setChecked( TRUE ); + } + else if( m_modulationAlgo1 == oscillator::AMP_MODULATION ) + { + m_am1OscBtn->setChecked( TRUE ); + } + else if( m_modulationAlgo1 == oscillator::MIX ) + { + m_mix1OscBtn->setChecked( TRUE ); + } + else if( m_modulationAlgo1 == oscillator::SYNC ) + { + m_sync1OscBtn->setChecked( TRUE ); + } + + QButtonGroup * modulation_algo_group1 = new QButtonGroup( this ); + modulation_algo_group1->addButton( m_fm1OscBtn ); + modulation_algo_group1->addButton( m_am1OscBtn ); + modulation_algo_group1->addButton( m_mix1OscBtn ); + modulation_algo_group1->addButton( m_sync1OscBtn ); + modulation_algo_group1->setExclusive( TRUE ); +#ifndef QT4 + modulation_algo_group1->hide(); +#endif + + m_fm2OscBtn = new pixmapButton( this ); + m_fm2OscBtn->move( 80, 70 ); + m_fm2OscBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "fm_active" ) ); + m_fm2OscBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "fm_inactive" ) ); + m_fm2OscBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). + createHeuristicMask() ) ); + connect( m_fm2OscBtn, SIGNAL( toggled( bool ) ), this, SLOT( + fm2BtnToggled( bool ) ) ); + toolTip::add( m_fm2OscBtn, tr( "use frequency modulation for " + "modulating oscillator 3 with " + "oscillator 2" ) ); + + m_am2OscBtn = new pixmapButton( this ); + m_am2OscBtn->move( 120, 70 ); + m_am2OscBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "am_active" ) ); + m_am2OscBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap("am_inactive" ) ); + m_am2OscBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). + createHeuristicMask() ) ); + connect( m_am2OscBtn, SIGNAL( toggled( bool ) ), this, + SLOT( am2BtnToggled( bool ) ) ); + toolTip::add( m_am2OscBtn, tr( "use amplitude modulation for " + "modulating oscillator 3 with " + "oscillator 2" ) ); + + m_mix2OscBtn = new pixmapButton( this ); + m_mix2OscBtn->move( 160, 70 ); + m_mix2OscBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "mix_active" ) ); + m_mix2OscBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "mix_inactive" ) ); + m_mix2OscBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). + createHeuristicMask() ) ); + connect( m_mix2OscBtn, SIGNAL( toggled( bool ) ), this, + SLOT( mix2BtnToggled( bool ) ) ); + toolTip::add( m_mix2OscBtn, tr("mix output of oscillator 2 & 3" ) ); + + m_sync2OscBtn = new pixmapButton( this ); + m_sync2OscBtn->move( 200, 70 ); + m_sync2OscBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "sync_active" ) ); + m_sync2OscBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "sync_inactive" ) ); + m_sync2OscBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). + createHeuristicMask() ) ); + connect( m_sync2OscBtn, SIGNAL( toggled( bool ) ), this, + SLOT( sync2BtnToggled( bool ) ) ); + toolTip::add( m_sync2OscBtn, tr( "synchronize oscillator 2 with " + "oscillator 3" ) ); + + if( m_modulationAlgo2 == oscillator::FREQ_MODULATION ) + { + m_fm2OscBtn->setChecked( TRUE ); + } + else if( m_modulationAlgo2 == oscillator::AMP_MODULATION ) + { + m_am2OscBtn->setChecked( TRUE ); + } + else if( m_modulationAlgo2 == oscillator::MIX ) + { + m_mix2OscBtn->setChecked( TRUE ); + } + else if( m_modulationAlgo2 == oscillator::SYNC ) + { + m_sync2OscBtn->setChecked( TRUE ); + } + + QButtonGroup * modulation_algo_group2 = new QButtonGroup( this ); + modulation_algo_group2->addButton( m_fm2OscBtn ); + modulation_algo_group2->addButton( m_am2OscBtn ); + modulation_algo_group2->addButton( m_mix2OscBtn ); + modulation_algo_group2->addButton( m_sync2OscBtn ); + modulation_algo_group2->setExclusive( TRUE ); +#ifndef QT4 + modulation_algo_group2->hide(); +#endif + + + for( int i = 0; i < NUM_OF_OSCILLATORS; ++i ) + { + // reset current m_osc-structure + m_osc[i].waveShape = oscillator::SIN_WAVE; + + // setup volume-knob + m_osc[i].volKnob = new knob( knobSmall_17, this, tr( + "Osc %1 volume" ).arg( i+1 ) ); + m_osc[i].volKnob->move( 6, 104+i*50 ); + m_osc[i].volKnob->setRange( MIN_VOLUME, MAX_VOLUME, 1.0f ); + m_osc[i].volKnob->setValue( DEFAULT_VOLUME, TRUE ); + m_osc[i].volKnob->setHintText( tr( "Osc %1 volume:" ).arg( + i+1 ) + " ", "%" ); +#ifdef QT4 + m_osc[i].volKnob->setWhatsThis( +#else + QWhatsThis::add( m_osc[i].volKnob, +#endif + tr( "With this knob you can set the volume of " + "oscillator %1. When setting a value of 0 the " + "oscillator is turned off. Otherwise you can " + "hear the oscillator as loud as you set it " + "here.").arg( i+1 ) ); + + // setup panning-knob + m_osc[i].panKnob = new knob( knobSmall_17, this, + tr( "Osc %1 panning" ).arg( i + 1 ) ); + m_osc[i].panKnob->move( 33, 104+i*50 ); + m_osc[i].panKnob->setRange( PANNING_LEFT, PANNING_RIGHT, 1.0f ); + m_osc[i].panKnob->setValue( DEFAULT_PANNING, TRUE ); + m_osc[i].panKnob->setHintText( tr("Osc %1 panning:").arg( i+1 ) + + " ", "" ); +#ifdef QT4 + m_osc[i].panKnob->setWhatsThis( +#else + QWhatsThis::add( m_osc[i].panKnob, +#endif + tr( "With this knob you can set the panning of the " + "oscillator %1. A value of -100 means 100% " + "left and a value of 100 moves oscillator-" + "output right.").arg( i+1 ) ); + + // setup coarse-knob + m_osc[i].coarseKnob = new knob( knobSmall_17, this, + tr("Osc %1 coarse detuning").arg( i + 1 ) ); + m_osc[i].coarseKnob->move( 66, 104 + i * 50 ); + m_osc[i].coarseKnob->setRange( -2 * NOTES_PER_OCTAVE, + 2 * NOTES_PER_OCTAVE, 1.0f ); + m_osc[i].coarseKnob->setValue( 0.0f, TRUE ); + m_osc[i].coarseKnob->setHintText( tr( "Osc %1 coarse detuning:" + ).arg( i + 1 ) + " ", + " " + tr( "semitones" ) ); +#ifdef QT4 + m_osc[i].coarseKnob->setWhatsThis( +#else + QWhatsThis::add( m_osc[i].coarseKnob, +#endif + tr( "With this knob you can set the coarse detuning of " + "oscillator %1. You can detune the oscillator " + "12 semitones (1 octave) up and down. This is " + "useful for creating sounds with a chord." ). + arg( i + 1 ) ); + + // setup knob for left fine-detuning + m_osc[i].fineLKnob = new knob( knobSmall_17, this, + tr( "Osc %1 fine detuning left" ).arg( i+1 ) ); + m_osc[i].fineLKnob->move( 90, 104 + i * 50 ); + m_osc[i].fineLKnob->setRange( -100.0f, 100.0f, 1.0f ); + m_osc[i].fineLKnob->setValue( 0.0f, TRUE ); + m_osc[i].fineLKnob->setHintText( tr( "Osc %1 fine detuning " + "left:" ).arg( i + 1 ) + + " ", " " + + tr( "cents" ) ); +#ifdef QT4 + m_osc[i].fineLKnob->setWhatsThis( +#else + QWhatsThis::add( m_osc[i].fineLKnob, +#endif + tr( "With this knob you can set the fine detuning of " + "oscillator %1 for the left channel. The fine-" + "detuning is ranged between -100 cents and " + "+100 cents. This is useful for creating " + "\"fat\" sounds." ).arg( i + 1 ) ); + + // setup knob for right fine-detuning + m_osc[i].fineRKnob = new knob( knobSmall_17, this, + tr( "Osc %1 fine detuning right" + ).arg( i + 1 ) ); + m_osc[i].fineRKnob->move( 110, 104 + i * 50 ); + m_osc[i].fineRKnob->setRange( -100.0f, 100.0f, 1.0f ); + m_osc[i].fineRKnob->setValue( 0.0f, TRUE ); + m_osc[i].fineRKnob->setHintText( tr( "Osc %1 fine detuning " + "right:").arg( i + 1 ) + + " ", " " + tr( "cents" ) ); +#ifdef QT4 + m_osc[i].fineRKnob->setWhatsThis( +#else + QWhatsThis::add( m_osc[i].fineRKnob, +#endif + tr( "With this knob you can set the fine detuning of " + "oscillator %1 for the right channel. The " + "fine-detuning is ranged between -100 cents " + "and +100 cents. This is useful for creating " + "\"fat\" sounds." ).arg( i+1 ) ); + + // setup phase-offset-knob + m_osc[i].phaseOffsetKnob = new knob( knobSmall_17, this, + tr( "Osc %1 phase-" + "offset" ).arg( i+1 ) ); + m_osc[i].phaseOffsetKnob->move( 142, 104 + i * 50 ); + m_osc[i].phaseOffsetKnob->setRange( 0.0f, 360.0f, 1.0f ); + m_osc[i].phaseOffsetKnob->setValue( 0.0f, TRUE ); + m_osc[i].phaseOffsetKnob->setHintText( tr( "Osc %1 phase-" + "offset:" ). + arg( i + 1 ) + + " ", " " + tr( "degrees" ) ); +#ifdef QT4 + m_osc[i].phaseOffsetKnob->setWhatsThis( +#else + QWhatsThis::add( m_osc[i].phaseOffsetKnob, +#endif + tr( "With this knob you can set the phase-offset of " + "oscillator %1. That means you can move the " + "point within an oscillation where the " + "oscillator begins to oscillate. For example " + "if you have a sine-wave and have a phase-" + "offset of 180 degrees the wave will first go " + "down. It's the same with a square-wave." + ).arg( i+1 ) ); + + // setup stereo-phase-detuning-knob + m_osc[i].stereoPhaseDetuningKnob = new knob( knobSmall_17, this, + tr( "Osc %1 stereo phase-" + "detuning" ).arg( i+1 ) + ); + m_osc[i].stereoPhaseDetuningKnob->move( 166, 104 + i * 50 ); + m_osc[i].stereoPhaseDetuningKnob->setRange( 0.0f, 360.0f, + 1.0f ); + m_osc[i].stereoPhaseDetuningKnob->setValue( 0.0f, TRUE ); + m_osc[i].stereoPhaseDetuningKnob->setHintText( tr("Osc %1 " + "stereo phase-" + "detuning:" ). + arg( i + 1 ) + + " ", " " + + tr( "degrees" ) ); +#ifdef QT4 + m_osc[i].stereoPhaseDetuningKnob->setWhatsThis( +#else + QWhatsThis::add( m_osc[i].stereoPhaseDetuningKnob, +#endif + tr( "With this knob you can set the stereo phase-" + "detuning of oscillator %1. The stereo phase-" + "detuning specifies the size of the difference " + "between the phase-offset of left and right " + "channel. This is very good for creating wide " + "stereo-sounds." ).arg( i+1 ) ); + + m_osc[i].sinWaveBtn = new pixmapButton( this ); + m_osc[i].sinWaveBtn->move( 188, 105 + i * 50 ); + m_osc[i].sinWaveBtn->setActiveGraphic( embed::getIconPixmap( + "sin_wave_active" ) ); + m_osc[i].sinWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "sin_wave_inactive" ) ); + m_osc[i].sinWaveBtn->setChecked( TRUE ); + toolTip::add( m_osc[i].sinWaveBtn, + tr( "Click here if you want a sine-wave for " + "current oscillator." ) ); + + m_osc[i].triangleWaveBtn = new pixmapButton( this ); + m_osc[i].triangleWaveBtn->move( 203, 105 + i * 50 ); + m_osc[i].triangleWaveBtn->setActiveGraphic( + embed::getIconPixmap( "triangle_wave_active" ) ); + m_osc[i].triangleWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "triangle_wave_inactive" ) ); + toolTip::add( m_osc[i].triangleWaveBtn, + tr( "Click here if you want a triangle-wave " + "for current oscillator." ) ); + + m_osc[i].sawWaveBtn = new pixmapButton( this ); + m_osc[i].sawWaveBtn->move( 218, 105 + i * 50 ); + m_osc[i].sawWaveBtn->setActiveGraphic( embed::getIconPixmap( + "saw_wave_active" ) ); + m_osc[i].sawWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "saw_wave_inactive" ) ); + toolTip::add( m_osc[i].sawWaveBtn, + tr( "Click here if you want a saw-wave for " + "current oscillator." ) ); + + m_osc[i].sqrWaveBtn = new pixmapButton( this ); + m_osc[i].sqrWaveBtn->move( 233, 105 + i * 50 ); + m_osc[i].sqrWaveBtn->setActiveGraphic( embed::getIconPixmap( + "square_wave_active" ) ); + m_osc[i].sqrWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "square_wave_inactive" ) ); + toolTip::add( m_osc[i].sqrWaveBtn, + tr( "Click here if you want a square-wave for " + "current oscillator." ) ); + + m_osc[i].moogSawWaveBtn = new pixmapButton( this ); + m_osc[i].moogSawWaveBtn->move( 188, 120+i*50 ); + m_osc[i].moogSawWaveBtn->setActiveGraphic( + embed::getIconPixmap( "moog_saw_wave_active" ) ); + m_osc[i].moogSawWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "moog_saw_wave_inactive" ) ); + toolTip::add( m_osc[i].moogSawWaveBtn, + tr( "Click here if you want a moog-saw-wave " + "for current oscillator." ) ); + + m_osc[i].expWaveBtn = new pixmapButton( this ); + m_osc[i].expWaveBtn->move( 203, 120+i*50 ); + m_osc[i].expWaveBtn->setActiveGraphic( embed::getIconPixmap( + "exp_wave_active" ) ); + m_osc[i].expWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "exp_wave_inactive" ) ); + toolTip::add( m_osc[i].expWaveBtn, + tr( "Click here if you want an exponential " + "wave for current oscillator." ) ); + + m_osc[i].whiteNoiseWaveBtn = new pixmapButton( this ); + m_osc[i].whiteNoiseWaveBtn->move( 218, 120+i*50 ); + m_osc[i].whiteNoiseWaveBtn->setActiveGraphic( + embed::getIconPixmap( "white_noise_wave_active" ) ); + m_osc[i].whiteNoiseWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "white_noise_wave_inactive" ) ); + toolTip::add( m_osc[i].whiteNoiseWaveBtn, + tr( "Click here if you want a white-noise for " + "current oscillator." ) ); + + m_osc[i].usrWaveBtn = new pixmapButton( this ); + m_osc[i].usrWaveBtn->move( 233, 120+i*50 ); + m_osc[i].usrWaveBtn->setActiveGraphic( embed::getIconPixmap( + "usr_wave_active" ) ); + m_osc[i].usrWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "usr_wave_inactive" ) ); + toolTip::add( m_osc[i].usrWaveBtn, + tr( "Click here if you want a user-defined " + "wave-shape for current oscillator." ) ); + + QButtonGroup * wave_btn_group = new QButtonGroup( this ); + wave_btn_group->addButton( m_osc[i].sinWaveBtn ); + wave_btn_group->addButton( m_osc[i].triangleWaveBtn ); + wave_btn_group->addButton( m_osc[i].sawWaveBtn ); + wave_btn_group->addButton( m_osc[i].sqrWaveBtn ); + wave_btn_group->addButton( m_osc[i].moogSawWaveBtn ); + wave_btn_group->addButton( m_osc[i].expWaveBtn ); + wave_btn_group->addButton( m_osc[i].whiteNoiseWaveBtn ); + wave_btn_group->addButton( m_osc[i].usrWaveBtn ); + wave_btn_group->setExclusive( TRUE ); +#ifndef QT4 + wave_btn_group->hide(); +#endif + + if( i == 0 ) + { // Osc 1 + connect( m_osc[i].sinWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc01SinWaveCh( bool ) ) ); + connect( m_osc[i].triangleWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc01TriangleWaveCh( bool ) ) ); + connect( m_osc[i].sawWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc01SawWaveCh( bool ) ) ); + connect( m_osc[i].sqrWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc01SquareWaveCh( bool ) ) ); + connect( m_osc[i].moogSawWaveBtn, + SIGNAL(toggled( bool ) ), this, + SLOT( osc01MoogSawWaveCh( bool ) ) ); + connect( m_osc[i].expWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc01ExpWaveCh( bool ) ) ); + connect( m_osc[i].whiteNoiseWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc01WhiteNoiseCh( bool ) ) ); + connect( m_osc[i].usrWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc01UserDefWaveCh( bool ) ) ); + connect( m_osc[i].usrWaveBtn, + SIGNAL( doubleClicked() ), this, + SLOT( osc01UserDefWaveDblClick() ) ); + } + else if( i == 1 ) + { // Osc 2 + connect( m_osc[i].sinWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc02SinWaveCh( bool ) ) ); + connect( m_osc[i].triangleWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc02TriangleWaveCh( bool ) ) ); + connect( m_osc[i].sawWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc02SawWaveCh( bool ) ) ); + connect( m_osc[i].sqrWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc02SquareWaveCh( bool ) ) ); + connect( m_osc[i].moogSawWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc02MoogSawWaveCh( bool ) ) ); + connect( m_osc[i].expWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc02ExpWaveCh( bool ) ) ); + connect( m_osc[i].whiteNoiseWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc02WhiteNoiseCh( bool ) ) ); + connect( m_osc[i].usrWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc02UserDefWaveCh( bool ) ) ); + connect( m_osc[i].usrWaveBtn, + SIGNAL( doubleClicked() ), this, + SLOT( osc02UserDefWaveDblClick() ) ); + } + else if( i == 2 ) + { // Osc 3 + connect( m_osc[i].sinWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc03SinWaveCh( bool ) ) ); + connect( m_osc[i].triangleWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc03TriangleWaveCh( bool ) ) ); + connect( m_osc[i].sawWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc03SawWaveCh( bool ) ) ); + connect( m_osc[i].sqrWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc03SquareWaveCh( bool ) ) ); + connect( m_osc[i].moogSawWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc03MoogSawWaveCh( bool ) ) ); + connect( m_osc[i].expWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc03ExpWaveCh( bool ) ) ); + connect( m_osc[i].whiteNoiseWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc03WhiteNoiseCh( bool ) ) ); + connect( m_osc[i].usrWaveBtn, + SIGNAL( toggled( bool ) ), this, + SLOT( osc03UserDefWaveCh( bool ) ) ); + connect( m_osc[i].usrWaveBtn, + SIGNAL( doubleClicked() ), this, + SLOT( osc03UserDefWaveDblClick() ) ); + } + } +} + + + + +tripleOscillator::~tripleOscillator() +{ +} + + + + +void tripleOscillator::saveSettings( QDomDocument & _doc, + QDomElement & _parent ) +{ + QDomElement to_de = _doc.createElement( nodeName() ); + to_de.setAttribute( "modalgo1", QString::number( m_modulationAlgo1 ) ); + to_de.setAttribute( "modalgo2", QString::number( m_modulationAlgo2 ) ); + + for( int i = 0; i < NUM_OF_OSCILLATORS; ++i ) + { + QString is = QString::number( i ); + to_de.setAttribute( "vol" + is, QString::number( + m_osc[i].volKnob->value() ) ); + to_de.setAttribute( "pan" + is, QString::number( + m_osc[i].panKnob->value() ) ); + to_de.setAttribute( "coarse" + is, QString::number( + m_osc[i].coarseKnob->value() ) ); + to_de.setAttribute( "finel" + is, QString::number( + m_osc[i].fineLKnob->value() ) ); + to_de.setAttribute( "finer" + is, QString::number( + m_osc[i].fineRKnob->value() ) ); + to_de.setAttribute( "phoffset" + is, QString::number( + m_osc[i].phaseOffsetKnob->value() ) ); + to_de.setAttribute( "stphdetun" + is, QString::number( + m_osc[i].stereoPhaseDetuningKnob->value() ) ); + to_de.setAttribute( "wavetype" + is, QString::number( + m_osc[i].waveShape ) ); + to_de.setAttribute( "userwavefile" + is, + m_osc[i].m_sampleBuffer.audioFile() ); + } + + _parent.appendChild( to_de ); +} + + + + +void tripleOscillator::loadSettings( const QDomElement & _this ) +{ + m_modulationAlgo1 = static_cast( + _this.attribute( "modalgo1" ).toInt() ); + m_modulationAlgo2 = static_cast( + _this.attribute( "modalgo2" ).toInt() ); + + getModulationButton( m_modulationAlgo1, 1 )->setChecked( TRUE ); + getModulationButton( m_modulationAlgo2, 2 )->setChecked( TRUE ); + + for( int i = 0; i < NUM_OF_OSCILLATORS; ++i ) + { + QString is = QString::number( i ); + m_osc[i].volKnob->setValue( _this.attribute( "vol" + is ). + toFloat() ); + m_osc[i].panKnob->setValue( _this.attribute( "pan" + is ). + toFloat() ); + m_osc[i].coarseKnob->setValue( _this.attribute( "coarse" + is ). + toFloat() ); + m_osc[i].fineLKnob->setValue( _this.attribute( "finel" + is ). + toFloat() ); + m_osc[i].fineRKnob->setValue( _this.attribute( "finer" + is ). + toFloat() ); + m_osc[i].phaseOffsetKnob->setValue( _this.attribute( + "phoffset" + is ).toFloat() ); + m_osc[i].stereoPhaseDetuningKnob->setValue( _this.attribute( + "stphdetun" + is ).toFloat() ); + m_osc[i].m_sampleBuffer.setAudioFile( _this.attribute( + "userwavefile" + is ) ); + switch( _this.attribute( "wavetype" + is ).toInt() ) + { + case oscillator::TRIANGLE_WAVE: + m_osc[i].triangleWaveBtn->setChecked( TRUE ); + break; + case oscillator::SAW_WAVE: + m_osc[i].sawWaveBtn->setChecked( TRUE ); + break; + case oscillator::SQUARE_WAVE: + m_osc[i].sqrWaveBtn->setChecked( TRUE ); + break; + case oscillator::MOOG_SAW_WAVE: + m_osc[i].moogSawWaveBtn->setChecked( TRUE ); + break; + case oscillator::EXP_WAVE: + m_osc[i].expWaveBtn->setChecked( TRUE ); + break; + case oscillator::WHITE_NOISE_WAVE: + m_osc[i].whiteNoiseWaveBtn->setChecked( TRUE ); + break; + case oscillator::USER_DEF_WAVE: + toolTip::add( m_osc[i].usrWaveBtn, + m_osc[i].m_sampleBuffer.audioFile() ); + m_osc[i].usrWaveBtn->setChecked( TRUE ); + break; + case oscillator::SIN_WAVE: + default: + m_osc[i].sinWaveBtn->setChecked( TRUE ); + break; + } + } +} + + + + +QString tripleOscillator::nodeName( void ) const +{ + return( tripleoscillator_plugin_descriptor.name ); +} + + + + +void tripleOscillator::playNote( notePlayHandle * _n ) +{ + if( _n->totalFramesPlayed() == 0 ) + { + float freq = getChannelTrack()->frequency( _n ); + + oscillator * oscs_l[NUM_OF_OSCILLATORS]; + oscillator * oscs_r[NUM_OF_OSCILLATORS]; + + for( Sint8 i = NUM_OF_OSCILLATORS-1; i >= 0; --i ) + { + + float osc_detuning_l = pow( 2.0, ( + (float)m_osc[i].coarseKnob->value() * 100.0f + + (float)m_osc[i].fineLKnob->value() ) / 1200.0f); + float osc_detuning_r = pow( 2.0, ( + (float)m_osc[i].coarseKnob->value() * 100.0f + + (float)m_osc[i].fineRKnob->value() ) / 1200.0f); + + float vol_fac_l = ( m_osc[i].panKnob->value() + + PANNING_RIGHT ) / 100.0f; + float vol_fac_r = ( PANNING_RIGHT - + m_osc[i].panKnob->value() ) / + 100.0f; + + if( vol_fac_l > 1.0f ) + { + vol_fac_l = 1.0f; + } + if( vol_fac_r > 1.0f ) + { + vol_fac_r = 1.0f; + } + + vol_fac_l *= m_osc[i].volKnob->value() / 100.0f; + vol_fac_r *= m_osc[i].volKnob->value() / 100.0f; + + // the third oscs needs no sub-oscs... + if( i == 2 ) + { + oscs_l[i] = oscillator::createOsc( + m_osc[i].waveShape, + oscillator::MIX, + freq*osc_detuning_l, + static_cast( + m_osc[i].phaseOffsetKnob->value() + + m_osc[i].stereoPhaseDetuningKnob->value() ), + vol_fac_l ); + oscs_r[i] = oscillator::createOsc( + m_osc[i].waveShape, + oscillator::MIX, + freq*osc_detuning_r, + static_cast( + m_osc[i].phaseOffsetKnob->value() ), + vol_fac_r ); + } + else + { + oscs_l[i] = oscillator::createOsc( + m_osc[i].waveShape, + getModulationAlgo( i + 1 ), + freq*osc_detuning_l, + static_cast( + m_osc[i].phaseOffsetKnob->value() + + m_osc[i].stereoPhaseDetuningKnob->value() ), + vol_fac_l, oscs_l[i + 1] ); + oscs_r[i] = oscillator::createOsc( + m_osc[i].waveShape, + getModulationAlgo( i + 1 ), + freq*osc_detuning_r, + static_cast( + m_osc[i].phaseOffsetKnob->value() ), + vol_fac_r, + oscs_r[i + 1] ); + } + + if( m_osc[i].waveShape == oscillator::USER_DEF_WAVE ) + { + oscs_l[i]->setUserWave( + m_osc[i].m_sampleBuffer.data(), + m_osc[i].m_sampleBuffer.frames() ); + oscs_r[i]->setUserWave( + m_osc[i].m_sampleBuffer.data(), + m_osc[i].m_sampleBuffer.frames() ); + } + + } + + _n->m_pluginData = new oscPtr; + static_cast( _n->m_pluginData )->oscLeft = oscs_l[0]; + static_cast< oscPtr *>( _n->m_pluginData )->oscRight = + oscs_r[0]; + } + + oscillator * osc_l = static_cast( _n->m_pluginData )->oscLeft; + oscillator * osc_r = static_cast( _n->m_pluginData + )->oscRight; + + const Uint32 frames = mixer::inst()->framesPerAudioBuffer(); + sampleFrame * buf = bufferAllocator::alloc( frames ); + + osc_l->update( buf, frames, 0 ); + osc_r->update( buf, frames, 1 ); + + getChannelTrack()->processAudioBuffer( buf, frames, _n ); + + bufferAllocator::free( buf ); +} + + + + +void tripleOscillator::deleteNotePluginData( notePlayHandle * _n ) +{ + if( _n->m_pluginData == NULL ) + { + return; + } + delete static_cast( static_cast( + _n->m_pluginData )->oscLeft ); + delete static_cast( static_cast( + _n->m_pluginData )->oscRight ); + delete static_cast( _n->m_pluginData ); +} + + + + +// now follows all the stupid UI-Code... + +void tripleOscillator::setModulationAlgo( + oscillator::modulationAlgos _new_modulation_algo, int _n ) +{ + if( _n == 1 ) + { + m_modulationAlgo1 = _new_modulation_algo; + } + else + { + m_modulationAlgo2 = _new_modulation_algo; + } + + songEditor::inst()->setModified(); +} + + + + +oscillator::modulationAlgos tripleOscillator::getModulationAlgo( int _n ) +{ + if( _n == 1 ) + { + return( m_modulationAlgo1 ); + } + else + { + return( m_modulationAlgo2 ); + } +} + + + + +void tripleOscillator::doSinWaveBtn( oscillatorData * _osc ) +{ + _osc->waveShape = oscillator::SIN_WAVE; + songEditor::inst()->setModified(); +} + + + + +void tripleOscillator::doTriangleWaveBtn( oscillatorData * _osc ) +{ + _osc->waveShape = oscillator::TRIANGLE_WAVE; + songEditor::inst()->setModified(); +} + + + + +void tripleOscillator::doSawWaveBtn( oscillatorData * _osc ) +{ + _osc->waveShape = oscillator::SAW_WAVE; + songEditor::inst()->setModified(); +} + + + + +void tripleOscillator::doSqrWaveBtn( oscillatorData * _osc ) +{ + _osc->waveShape = oscillator::SQUARE_WAVE; + songEditor::inst()->setModified(); +} + + + + +void tripleOscillator::doMoogSawWaveBtn( oscillatorData * _osc ) +{ + _osc->waveShape = oscillator::MOOG_SAW_WAVE; + songEditor::inst()->setModified(); +} + + + + +void tripleOscillator::doExpWaveBtn( oscillatorData * _osc ) +{ + _osc->waveShape = oscillator::EXP_WAVE; + songEditor::inst()->setModified(); +} + + + + +void tripleOscillator::doWhiteNoiseWaveBtn( oscillatorData * _osc ) +{ + _osc->waveShape = oscillator::WHITE_NOISE_WAVE; + songEditor::inst()->setModified(); +} + + + + +void tripleOscillator::doUsrWaveBtn( oscillatorData * _osc ) +{ + _osc->waveShape = oscillator::USER_DEF_WAVE; + songEditor::inst()->setModified(); +} + + + +// Slots for Osc 1 +void tripleOscillator::osc01SinWaveCh( bool _on ) +{ + if( _on ) doSinWaveBtn( &m_osc[0] ); +} + +void tripleOscillator::osc01TriangleWaveCh( bool _on ) +{ + if( _on ) doTriangleWaveBtn( &m_osc[0] ); +} + +void tripleOscillator::osc01SawWaveCh( bool _on ) +{ + if( _on ) doSawWaveBtn( &m_osc[0] ); +} + +void tripleOscillator::osc01SquareWaveCh( bool _on ) +{ + if( _on ) doSqrWaveBtn( &m_osc[0] ); +} + +void tripleOscillator::osc01MoogSawWaveCh( bool _on ) +{ + if( _on ) doMoogSawWaveBtn( &m_osc[0] ); +} + +void tripleOscillator::osc01ExpWaveCh( bool _on ) +{ + if( _on ) doExpWaveBtn( &m_osc[0] ); +} + +void tripleOscillator::osc01WhiteNoiseCh( bool _on ) +{ + if( _on ) doWhiteNoiseWaveBtn( &m_osc[0] ); +} + +void tripleOscillator::osc01UserDefWaveCh( bool _on ) +{ + if( _on ) doUsrWaveBtn( &m_osc[0] ); +} + +void tripleOscillator::osc01UserDefWaveDblClick( void ) +{ + QString af = m_osc[0].m_sampleBuffer.openAudioFile(); + if( af != "" ) + { + m_osc[0].m_sampleBuffer.setAudioFile( af ); +/*#ifndef QT4 + toolTip::remove( m_osc[0].usrWaveBtn ); +#endif*/ + toolTip::add( m_osc[0].usrWaveBtn, + m_osc[0].m_sampleBuffer.audioFile() ); + } +} + + + +// Slots for Osc 2 +void tripleOscillator::osc02SinWaveCh( bool _on ) +{ + if( _on ) doSinWaveBtn( &m_osc[1] ); +} + +void tripleOscillator::osc02TriangleWaveCh( bool _on ) +{ + if( _on ) doTriangleWaveBtn( &m_osc[1] ); +} + +void tripleOscillator::osc02SawWaveCh( bool _on ) +{ + if( _on ) doSawWaveBtn( &m_osc[1] ); +} + +void tripleOscillator::osc02SquareWaveCh( bool _on ) +{ + if( _on ) doSqrWaveBtn( &m_osc[1] ); +} + +void tripleOscillator::osc02MoogSawWaveCh( bool _on ) +{ + if( _on ) doMoogSawWaveBtn( &m_osc[1] ); +} + +void tripleOscillator::osc02ExpWaveCh( bool _on ) +{ + if( _on ) doExpWaveBtn( &m_osc[1] ); +} + +void tripleOscillator::osc02WhiteNoiseCh( bool _on ) +{ + if( _on ) doWhiteNoiseWaveBtn( &m_osc[1] ); +} + +void tripleOscillator::osc02UserDefWaveCh( bool _on ) +{ + if( _on ) doUsrWaveBtn( &m_osc[1] ); +} + +void tripleOscillator::osc02UserDefWaveDblClick( void ) +{ + QString af = m_osc[1].m_sampleBuffer.openAudioFile(); + if( af != "" ) + { + m_osc[1].m_sampleBuffer.setAudioFile( af ); +/*#ifndef QT4 + toolTip::remove( m_osc[1].usrWaveBtn ); +#endif*/ + toolTip::add( m_osc[1].usrWaveBtn, + m_osc[1].m_sampleBuffer.audioFile() ); + } +} + + +// Slots for Osc 3 +void tripleOscillator::osc03SinWaveCh( bool _on ) +{ + if( _on ) doSinWaveBtn( &m_osc[2] ); +} + +void tripleOscillator::osc03TriangleWaveCh( bool _on ) +{ + if( _on ) doTriangleWaveBtn( &m_osc[2] ); +} + +void tripleOscillator::osc03SawWaveCh( bool _on ) +{ + if( _on ) doSawWaveBtn( &m_osc[2] ); +} + +void tripleOscillator::osc03SquareWaveCh( bool _on ) +{ + if( _on ) doSqrWaveBtn( &m_osc[2] ); +} + +void tripleOscillator::osc03MoogSawWaveCh( bool _on ) +{ + if( _on ) doMoogSawWaveBtn( &m_osc[2] ); +} + +void tripleOscillator::osc03ExpWaveCh( bool _on ) +{ + if( _on ) doExpWaveBtn( &m_osc[2] ); +} + +void tripleOscillator::osc03WhiteNoiseCh( bool _on ) +{ + if( _on ) doWhiteNoiseWaveBtn( &m_osc[2] ); +} + +void tripleOscillator::osc03UserDefWaveCh( bool _on ) +{ + if( _on ) doUsrWaveBtn( &m_osc[2] ); +} + +void tripleOscillator::osc03UserDefWaveDblClick( void ) +{ + QString af = m_osc[2].m_sampleBuffer.openAudioFile(); + if( af != "" ) + { + m_osc[2].m_sampleBuffer.setAudioFile( af ); +/*#ifndef QT4 + toolTip::remove( m_osc[2].usrWaveBtn ); +#endif*/ + toolTip::add( m_osc[2].usrWaveBtn, + m_osc[2].m_sampleBuffer.audioFile() ); + } +} + + + + +void tripleOscillator::fm1BtnToggled( bool _on ) +{ + if( _on ) setModulationAlgo( oscillator::FREQ_MODULATION, 1 ); +} + + + +void tripleOscillator::am1BtnToggled( bool _on ) +{ + if( _on ) setModulationAlgo( oscillator::AMP_MODULATION, 1 ); +} + + + +void tripleOscillator::mix1BtnToggled( bool _on ) +{ + if( _on ) setModulationAlgo( oscillator::MIX, 1 ); +} + + + +void tripleOscillator::sync1BtnToggled( bool _on ) +{ + if( _on ) setModulationAlgo( oscillator::SYNC, 1 ); +} + + + +void tripleOscillator::fm2BtnToggled( bool _on ) +{ + if( _on ) setModulationAlgo( oscillator::FREQ_MODULATION, 2 ); +} + + + +void tripleOscillator::am2BtnToggled( bool _on ) +{ + if( _on ) setModulationAlgo( oscillator::AMP_MODULATION, 2 ); +} + + + +void tripleOscillator::mix2BtnToggled( bool _on ) +{ + if( _on ) setModulationAlgo( oscillator::MIX, 2 ); +} + + + +void tripleOscillator::sync2BtnToggled( bool _on ) +{ + if( _on ) setModulationAlgo( oscillator::SYNC, 2 ); +} + + + + +pixmapButton * tripleOscillator::getModulationButton( + oscillator::modulationAlgos _modulation_algo, int _n ) +{ + if( _n == 1 ) + { + switch( _modulation_algo ) + { + case oscillator::FREQ_MODULATION: return( m_fm1OscBtn ); + case oscillator::AMP_MODULATION: return( m_am1OscBtn ); + case oscillator::MIX: return( m_mix1OscBtn ); + case oscillator::SYNC: return( m_sync1OscBtn ); + } + } + else + { + switch( _modulation_algo ) + { + case oscillator::FREQ_MODULATION: return( m_fm2OscBtn ); + case oscillator::AMP_MODULATION: return( m_am2OscBtn ); + case oscillator::MIX: return( m_mix2OscBtn ); + case oscillator::SYNC: return( m_sync2OscBtn ); + } + } +#ifdef LMMS_DEBUG + // there's something really not ok, if this case occurs, so let's exit + assert( 1 != 1 ); +#endif + return( NULL ); +} + + + + +extern "C" +{ + +// neccessary for getting instance out of shared lib +plugin * lmms_plugin_main( void * _data ) +{ + return( new tripleOscillator( + static_cast( _data ) ) ); +} + +} + + +#undef setChecked + + +#include "triple_oscillator.moc" + diff --git a/plugins/triple_oscillator/triple_oscillator.h b/plugins/triple_oscillator/triple_oscillator.h new file mode 100644 index 000000000..cb8b873b0 --- /dev/null +++ b/plugins/triple_oscillator/triple_oscillator.h @@ -0,0 +1,167 @@ +/* + * triple_oscillator.h - declaration of class tripleOscillator a powerful + * instrument-plugin with 3 oscillators + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef _TRIPLE_OSCILLATOR_H +#define _TRIPLE_OSCILLATOR_H + + +#include "instrument.h" +#include "oscillator.h" +#include "sample_buffer.h" + + +class knob; +class pixmapButton; +class notePlayHandle; + + +const int NUM_OF_OSCILLATORS = 3; + + +class tripleOscillator : public instrument +{ + Q_OBJECT +public: + tripleOscillator( channelTrack * _channel ) FASTCALL; + virtual ~tripleOscillator(); + + virtual void FASTCALL playNote( notePlayHandle * _n ); + virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + virtual QString nodeName( void ) const; + + +protected slots: + // Slots for Osc 1 + void osc01SinWaveCh( bool _on ); + void osc01TriangleWaveCh( bool _on ); + void osc01SawWaveCh( bool _on ); + void osc01SquareWaveCh( bool _on ); + void osc01MoogSawWaveCh( bool _on ); + void osc01ExpWaveCh( bool _on ); + void osc01WhiteNoiseCh( bool _on ); + void osc01UserDefWaveCh( bool _on ); + void osc01UserDefWaveDblClick( void ); + + // Slots for Osc 2 + void osc02SinWaveCh( bool _on ); + void osc02TriangleWaveCh( bool _on ); + void osc02SawWaveCh( bool _on ); + void osc02SquareWaveCh( bool _on ); + void osc02MoogSawWaveCh( bool _on ); + void osc02ExpWaveCh( bool _on ); + void osc02WhiteNoiseCh( bool _on ); + void osc02UserDefWaveCh( bool _on ); + void osc02UserDefWaveDblClick( void ); + + // Slots for Osc 3 + void osc03SinWaveCh( bool _on ); + void osc03TriangleWaveCh( bool _on ); + void osc03SawWaveCh( bool _on ); + void osc03SquareWaveCh( bool _on ); + void osc03MoogSawWaveCh( bool _on ); + void osc03ExpWaveCh( bool _on ); + void osc03WhiteNoiseCh( bool _on ); + void osc03UserDefWaveCh( bool _on ); + void osc03UserDefWaveDblClick( void ); + + // modulation-type-button slots + void fm1BtnToggled( bool _on ); + void am1BtnToggled( bool _on ); + void mix1BtnToggled( bool _on ); + void sync1BtnToggled( bool _on ); + + void fm2BtnToggled( bool _on ); + void am2BtnToggled( bool _on ); + void mix2BtnToggled( bool _on ); + void sync2BtnToggled( bool _on ); + + +private: + channelTrack * m_channelTrack; + + struct oscillatorData + { + oscillator::waveShapes waveShape; + knob * volKnob; + knob * panKnob; + knob * coarseKnob; + knob * fineLKnob; + knob * fineRKnob; + knob * phaseOffsetKnob; + knob * stereoPhaseDetuningKnob; + pixmapButton * sinWaveBtn; + pixmapButton * triangleWaveBtn; + pixmapButton * sawWaveBtn; + pixmapButton * sqrWaveBtn; + pixmapButton * moogSawWaveBtn; + pixmapButton * expWaveBtn; + pixmapButton * whiteNoiseWaveBtn; + pixmapButton * usrWaveBtn; + sampleBuffer m_sampleBuffer; + } m_osc[NUM_OF_OSCILLATORS]; + + struct oscPtr + { + oscillator * oscLeft; + oscillator * oscRight; + } ; + + + void FASTCALL doSinWaveBtn( oscillatorData * _osc ); + void FASTCALL doTriangleWaveBtn( oscillatorData * _osc ); + void FASTCALL doSawWaveBtn( oscillatorData * _osc ); + void FASTCALL doSqrWaveBtn( oscillatorData * _osc ); + void FASTCALL doMoogSawWaveBtn( oscillatorData * _osc ); + void FASTCALL doExpWaveBtn( oscillatorData * _osc ); + void FASTCALL doWhiteNoiseWaveBtn( oscillatorData * _osc ); + void FASTCALL doUsrWaveBtn( oscillatorData * _osc ); + pixmapButton * FASTCALL getModulationButton( + oscillator::modulationAlgos _modulation_algo, int _n ); + void FASTCALL setModulationAlgo( + oscillator::modulationAlgos _new_modulation_algo, int _n ); + oscillator::modulationAlgos FASTCALL getModulationAlgo( int _n ); + + + pixmapButton * m_fm1OscBtn; + pixmapButton * m_am1OscBtn; + pixmapButton * m_mix1OscBtn; + pixmapButton * m_sync1OscBtn; + pixmapButton * m_fm2OscBtn; + pixmapButton * m_am2OscBtn; + pixmapButton * m_mix2OscBtn; + pixmapButton * m_sync2OscBtn; + + oscillator::modulationAlgos m_modulationAlgo1; + oscillator::modulationAlgos m_modulationAlgo2; + +} ; + + +#endif diff --git a/plugins/vestige/Makefile.am b/plugins/vestige/Makefile.am new file mode 100644 index 000000000..d2a4cc944 --- /dev/null +++ b/plugins/vestige/Makefile.am @@ -0,0 +1,35 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I. + + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="vestige" + + +%.moc: ./%.h + $(MOC) -o $@ $< + + +MOC_FILES = ./vestige.moc + +BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h +EMBEDDED_RESOURCES = $(wildcard *png) + +./embedded_resources.h: $(EMBEDDED_RESOURCES) + $(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@ + +EXTRA_DIST = $(EMBEDDED_RESOURCES) + + +CLEANFILES = $(MOC_FILES) ./embedded_resources.h + + + +pkglib_LTLIBRARIES= libvestige.la + +libvestige_la_SOURCES = vestige.cpp vestige.h +# libvestige_la_LIBADD = -lfst + +$(libvestige_la_SOURCES): ./embedded_resources.h + diff --git a/plugins/vestige/artwork.png b/plugins/vestige/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..25893f82e1548c8d7b769cbfcf32886dc38a6eed GIT binary patch literal 11857 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 + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include + +#endif + + +#include "vestige.h" +#include "channel_track.h" +#include "note_play_handle.h" +#include "buffer_allocator.h" +#include "mixer.h" +#include "song_editor.h" +#include "instrument_play_handle.h" +#include "pixmap_button.h" +#include "tooltip.h" +#include "spc_bg_hndl_widget.h" + + +#include "embed.cpp" + + +extern "C" +{ + +plugin::descriptor vestige_plugin_descriptor = +{ + STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), + "VeSTige", + QT_TRANSLATE_NOOP( "plugin", + "experimental VST-hoster for using VST-plugins " + "within LMMS" ), + "Tobias Doerffel ", + 0x0100, + plugin::INSTRUMENT, + PLUGIN_NAME::findEmbeddedData( "logo.png" ) +} ; + +} + + +bool vestigeInstrument::s_initialized = FALSE; +bool vestigeInstrument::s_threadAdopted = FALSE; +QPixmap * vestigeInstrument::s_artwork = NULL; + + +vestigeInstrument::vestigeInstrument( channelTrack * _channel_track ) : + instrument( _channel_track, vestige_plugin_descriptor.public_name ), + specialBgHandlingWidget( PLUGIN_NAME::getIconPixmap( "artwork" ) ), + m_handle( NULL ), + m_fst( NULL ) +{ + if( s_artwork == NULL ) + { + s_artwork = new QPixmap( PLUGIN_NAME::getIconPixmap( + "artwork" ) ); + } +#ifdef QT4 + QPalette pal; + pal.setBrush( backgroundRole(), *s_artwork); + setPalette( pal ); +#else + setErasePixmap( *s_artwork ); +#endif + + m_openPluginButton = new pixmapButton( this ); + m_openPluginButton->setCheckable( FALSE ); + m_openPluginButton->setCursor( PointingHandCursor ); + m_openPluginButton->move( 200, 70 ); + m_openPluginButton->setActiveGraphic( embed::getIconPixmap( + "project_open_down" ) ); + m_openPluginButton->setInactiveGraphic( embed::getIconPixmap( + "project_open" ) ); + m_openPluginButton->setBgGraphic( getBackground( + m_openPluginButton ) ); + connect( m_openPluginButton, SIGNAL( clicked() ), this, + SLOT( openPlugin() ) ); + toolTip::add( m_openPluginButton, tr( "Open other VST-plugin" ) ); + +#ifdef QT4 + m_openPluginButton->setWhatsThis( +#else + QWhatsThis::add( m_openPluginButton, +#endif + tr( "Click here, if you want to open another VST-plugin. After " + "clicking on this button, a file-open-dialog appears " + "and you can select your file." ) ); + + if( s_initialized == FALSE ) + { + if( fst_init( fst_signal_handler ) ) + { + // TODO: message-box + return; + } + s_initialized = TRUE; + } + // now we need a play-handle which cares for calling play() + instrumentPlayHandle * iph = new instrumentPlayHandle( this ); + mixer::inst()->addPlayHandle( iph ); +} + + + + +vestigeInstrument::~vestigeInstrument() +{ + // this single call automates the rest of cleanup like trashing our + // play-handle and so on + invalidate(); + closePlugin(); +} + + + + +void vestigeInstrument::loadSettings( const QDomElement & _this ) +{ +} + + + + +void vestigeInstrument::saveSettings( QDomDocument & _doc, + QDomElement & _parent ) +{ + QDomElement vst_de = _doc.createElement( nodeName() ); + _parent.appendChild( vst_de ); +} + + + + +QString vestigeInstrument::nodeName( void ) const +{ + return( vestige_plugin_descriptor.name ); +} + + + + +void vestigeInstrument::setParameter( const QString & _param, + const QString & _value ) +{ + if( _param == "plugin" ) + { + closePlugin(); + + m_plugin = _value; + if( ( m_handle = fst_load( +#ifdef QT4 + m_plugin.constData().toAscii() +#else + m_plugin.ascii() +#endif + ) ) == NULL ) + { + QMessageBox::information( this, + tr( "Failed loading plugin" ), + tr( "The VST-plugin %1 couldn't be " + "loaded for some reason." ).arg( + m_plugin ), + QMessageBox::Ok ); + return; + } + if( ( m_fst = fst_instantiate( m_handle, hostCallback, + this ) ) == NULL ) + { + QMessageBox::information( this, + tr( "Failed instantiating plugin" ), + tr( "Couldn't create an instance of " + "VST-plugin %1 for some " + "reason." ).arg( m_plugin ), + QMessageBox::Ok ); + fst_unload( m_handle ); + m_handle = NULL; + return; + } + + // set sample-rate and blocksize + m_fst->plugin->dispatcher( m_fst->plugin, effSetSampleRate, 0, + 0, NULL, mixer::inst()->sampleRate() ); + m_fst->plugin->dispatcher( m_fst->plugin, effSetBlockSize, 0, + mixer::inst()->framesPerAudioBuffer(), + NULL, 0.0f ); + // set program to zero + m_fst->plugin->dispatcher( m_fst->plugin, effSetProgram, 0, 0, + NULL, 0.0f ); + // resume + m_fst->plugin->dispatcher( m_fst->plugin, effMainsChanged, 0, + 1, NULL, 0.0f ); +/* if( fst_run_editor( m_fst ) ) + { + printf( "VeSTige: cannot create editor\n" ); + }*/ + int vst_version = m_fst->plugin->dispatcher( m_fst->plugin, + effGetVstVersion, 0, 0, + NULL, 0.0f ); + if( vst_version < 2 ) + { + QMessageBox::information( this, + tr( "VST-plugin too old" ), + tr( "The version of VST-plugin %1 " + "is smaller than 2, which " + "isn't supported." ).arg( + m_plugin ), + QMessageBox::Ok ); + return; + } +/* printf("WID:%d %d\n", (int)fst_get_XID( m_fst ), + (int)QWidget::find( fst_get_XID( m_fst ) ) );*/ + + //printf("%d\n", m_fst->plugin->numParams); + } +} + + + + +void vestigeInstrument::play( void ) +{ + // the very first time, we have to adopt fst-thread + if( !s_threadAdopted ) + { + fst_adopt_thread(); + s_threadAdopted = TRUE; + } + + if( m_handle == NULL || m_fst == NULL ) + { + return; + } + + // first we gonna post all MIDI-events we enqueued so far + if( m_midiEvents.size() ) + { + // since MIDI-events are not received immediately, we have + // to have them stored somewhere even after dispatcher-call, + // so we create static copies of the data and post them + static VstEvents events; + static vvector cur_events; + cur_events = m_midiEvents; + m_midiEvents.clear(); + for( csize i = 0; i < cur_events.size(); ++i ) + { + events.events[i] = (VstEvent *) &cur_events[i]; + } + events.numEvents = cur_events.size(); + events.reserved = 0; + m_fst->plugin->dispatcher( m_fst->plugin, effProcessEvents, + 0, 0, &events, 0.0f ); + } + + // now we're ready to fetch sound from VST-plugin + const Uint32 frames = mixer::inst()->framesPerAudioBuffer(); + + int ch_in = m_fst->plugin->numInputs; + int ch_out = m_fst->plugin->numOutputs; + float * ins[ch_in]; + float * outs[ch_out]; + for( int i = 0; i < ch_in; ++i ) + { + ins[i] = bufferAllocator::alloc( frames ); + } + for( int i = 0; i < ch_out; ++i ) + { + outs[i] = bufferAllocator::alloc( frames ); + } + + if( m_fst->plugin->flags & effFlagsCanReplacing ) + { + m_fst->plugin->processReplacing( m_fst->plugin, ins, outs, + frames ); + + } + else + { + printf("normal process\n"); + //mixer::inst()->clearAudioBuffer( buf, frames ); + m_fst->plugin->process( m_fst->plugin, ins, outs, frames ); + printf("normal process done\n"); + } + + for( int i = 0; i < ch_in; ++i ) + { + bufferAllocator::free( ins[i] ); + } + + // got our data, now we just have to merge the 1/2 out-buffers + sampleFrame * buf = bufferAllocator::alloc( frames ); + Uint8 chnls = tMax( ch_out, DEFAULT_CHANNELS ); + if( chnls != DEFAULT_CHANNELS ) + { + mixer::inst()->clearAudioBuffer( buf, frames ); + } + + for( Uint32 f = 0; f < frames; ++f ) + { + for( Uint8 chnl = 0; chnl < chnls; ++chnl ) + { + buf[f][chnl] = outs[chnl][f]; + } + } + + for( int i = 0; i < ch_out; ++i ) + { + bufferAllocator::free( outs[i] ); + } + + getChannelTrack()->processAudioBuffer( buf, frames, NULL ); + + bufferAllocator::free( buf ); +} + + + + + + +void vestigeInstrument::playNote( notePlayHandle * _n ) +{ + if( _n->totalFramesPlayed() == 0 ) + { + enqueueEvent( midiEvent( NOTE_ON, 0, _n->key(), + _n->getVolume() ), _n->framesAhead() ); + } +} + + + + +void vestigeInstrument::deleteNotePluginData( notePlayHandle * _n ) +{ + enqueueEvent( midiEvent( NOTE_OFF, 0, _n->key(), 0 ) ); +} + + + + +void vestigeInstrument::openPlugin( void ) +{ +#ifdef QT4 + QFileDialog ofd( NULL, tr( "Open VST-plugin" ) ); +#else + QFileDialog ofd( QString::null, QString::null, NULL, "", TRUE ); + ofd.setWindowTitle( tr( "Open VST-plugin" ) ); +#endif + + QString dir; + if( m_plugin != "" ) + { +#ifdef QT4 + dir = QFileInfo( m_plugin ).absolutePath(); +#else + dir = QFileInfo( m_plugin ).dirPath( TRUE ); +#endif + } + else + { + dir = QDir::home().path(); + } + // change dir to position of previously opened file + ofd.setDirectory( dir ); + ofd.setFileMode( QFileDialog::ExistingFiles ); + + // set filters +#ifdef QT4 + QStringList types; + types << tr( "DLL-files (*.dll)" ) + << tr( "EXE-files (*.exe)" ) + ; + ofd.setFilters( types ); +#else + ofd.addFilter( tr( "DLL-files (*.dll)" ) ); + ofd.addFilter( tr( "EXE-files (*.exe)" ) ); + ofd.setSelectedFilter( tr( "DLL-files (*.dll)" ) ); +#endif + if( m_plugin != "" ) + { + // select previously opened file + ofd.selectFile( QFileInfo( m_plugin ).fileName() ); + } + + if ( ofd.exec () == QDialog::Accepted ) + { + if( ofd.selectedFiles().isEmpty() ) + { + return; + } + setParameter( "plugin", ofd.selectedFiles()[0] ); + } +} + + + + +void vestigeInstrument::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap pm( rect().size() ); + pm.fill( this, rect().topLeft() ); + + QPainter p( &pm, this ); +#endif + + p.drawPixmap( 0, 0, *s_artwork ); + + QString plugin_name = ( m_handle != NULL && m_fst != NULL ) ? + QString( m_handle->name ) + " " + + QString::number( m_fst->plugin->dispatcher( + m_fst->plugin, + effGetVendorVersion, + 0, 0, NULL, 0.0f ) ): + tr( "No VST-plugin loaded" ); + QFont f = p.font(); + f.setBold( TRUE ); + p.setFont( pointSize<10>( f ) ); + p.setPen( QColor( 0, 0, 0 ) ); + + p.drawText( 20, 80, plugin_name ); + + if( m_handle != NULL && m_fst != NULL ) + { + p.setPen( QColor( 64, 128, 64 ) ); + f.setBold( FALSE ); + p.setFont( pointSize<8>( f ) ); + char buf[1024]; + m_fst->plugin->dispatcher( m_fst->plugin, effGetVendorString, + 0, 0, buf, 0 ); + p.drawText( 20, 94, tr( "by" ) + " " + QString( buf ) ); + } +#ifndef QT4 + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void vestigeInstrument::closePlugin( void ) +{ + if( m_fst != NULL && m_handle != NULL ) + { + //fst_destroy_editor( m_fst ); + printf( "closing VST-plugin" ); + fst_close( m_fst ); + printf( "unloading VST-plugin" ); + fst_unload( m_handle ); + m_fst = NULL; + m_handle = NULL; + } +} + + + + +void vestigeInstrument::enqueueEvent( const midiEvent & _e, + Uint32 _frames_ahead ) +{ + if( m_handle == NULL || m_fst == NULL ) + { + return; + } + + VstMidiEvent event; + + event.type = kVstMidiType; + event.byteSize = 24; + event.deltaFrames = _frames_ahead; + event.flags = 0; + event.detune = 0; + event.noteLength = 0; + event.noteOffset = 0; + event.noteOffVelocity = 0; + event.reserved1 = 0; + event.reserved2 = 0; + event.midiData[0] = _e.m_type + _e.m_channel; + event.midiData[1] = _e.key(); + event.midiData[2] = _e.velocity(); + event.midiData[3] = 0; + + m_midiEvents.push_back( event ); +} + + + +#define DEBUG_CALLBACKS +#ifdef DEBUG_CALLBACKS +#define SHOW_CALLBACK printf +#else +#define SHOW_CALLBACK(...) +#endif + +long vestigeInstrument::hostCallback( AEffect * _effect, long _opcode, + long _index, long _value, void * _ptr, + float _opt ) +{ + static VstTimeInfo _timeInfo; + + SHOW_CALLBACK( "host-callback, opcode = %d", (int) _opcode ); + + switch( _opcode ) + { + case audioMasterAutomate: + SHOW_CALLBACK( "amc: audioMasterAutomate\n" ); + // index, value, returns 0 + _effect->setParameter( _effect, _index, _opt ); + return( 0 ); + + case audioMasterVersion: + SHOW_CALLBACK( "amc: audioMasterVersion\n" ); + // vst version, currently 2 (0 for older) + return( 2 ); + + case audioMasterCurrentId: + SHOW_CALLBACK( "amc: audioMasterCurrentId\n" ); + // returns the unique id of a plug that's currently + // loading + return( 0 ); + + case audioMasterIdle: + SHOW_CALLBACK ("amc: audioMasterIdle\n"); + // call application idle routine (this will + // call effEditIdle for all open editors too) + _effect->dispatcher( _effect, effEditIdle, 0, 0, NULL, + 0.0f ); + return( 0 ); + + case audioMasterPinConnected: + SHOW_CALLBACK( "amc: audioMasterPinConnected\n" ); + // inquire if an input or output is beeing connected; + // index enumerates input or output counting from zero: + // value is 0 for input and != 0 otherwise. note: the + // return value is 0 for such that older versions + // will always return true. + return( 1 ); + + case audioMasterWantMidi: + SHOW_CALLBACK( "amc: audioMasterWantMidi\n" ); + // is a filter which is currently ignored + return( 0 ); + + case audioMasterGetTime: + SHOW_CALLBACK( "amc: audioMasterGetTime\n" ); + // returns const VstTimeInfo* (or 0 if not supported) + // should contain a mask indicating which + // fields are required (see valid masks above), as some + // items may require extensive conversions + + memset( &_timeInfo, 0, sizeof( _timeInfo ) ); + + //tstate = jack_transport_query (jackvst->client, &jack_pos); + _timeInfo.samplePos = 0; + _timeInfo.sampleRate = mixer::inst()->sampleRate(); + _timeInfo.flags = 0; + _timeInfo.tempo = songEditor::inst()->getBPM(); + _timeInfo.timeSigNumerator = 4;//(long) floor (jack_pos.beats_per_bar); + _timeInfo.timeSigDenominator = 4;//(long) floor (jack_pos.beat_type); + _timeInfo.flags |= (kVstBarsValid|kVstTempoValid); +// if (tstate == JackTransportRolling) { + _timeInfo.flags |= kVstTransportPlaying; +// } + + return( (long)&_timeInfo ); + + case audioMasterProcessEvents: + SHOW_CALLBACK( "amc: audioMasterProcessEvents\n" ); + // VstEvents* in + return( 0 ); + + case audioMasterSetTime: + SHOW_CALLBACK( "amc: audioMasterSetTime\n" ); + // VstTimenfo* in , filter in , not + // supported + + case audioMasterTempoAt: + SHOW_CALLBACK( "amc: audioMasterTempoAt\n" ); + // returns tempo (in bpm * 10000) at sample frame + // location passed in + return( 0 ); + + case audioMasterGetNumAutomatableParameters: + SHOW_CALLBACK( "amc: audioMasterGetNumAutomatable" + "Parameters\n" ); + return( 0 ); + + case audioMasterGetParameterQuantization: + SHOW_CALLBACK( "amc: audioMasterGetParameter\n" + "Quantization\n" ); + // returns the integer value for +1.0 representation, + // or 1 if full single float precision is maintained + // in automation. parameter index in (-1: all, + // any) + return( 0 ); + + case audioMasterIOChanged: + SHOW_CALLBACK( "amc: audioMasterIOChanged\n" ); + // numInputs and/or numOutputs has changed + return( 0 ); + + case audioMasterNeedIdle: + SHOW_CALLBACK( "amc: audioMasterNeedIdle\n" ); + // plug needs idle calls (outside its editor window) + return( 0 ); + + case audioMasterSizeWindow: + // TODO using lmms-main-window-size + SHOW_CALLBACK( "amc: audioMasterSizeWindow\n" ); + // index: width, value: height + return( 0 ); + + case audioMasterGetSampleRate: + // TODO using mixer-call + SHOW_CALLBACK( "amc: audioMasterGetSampleRate\n" ); + return( 0 ); + + case audioMasterGetBlockSize: + // TODO using mixer-call + SHOW_CALLBACK( "amc: audioMasterGetBlockSize\n" ); + return( 0 ); + + case audioMasterGetInputLatency: + // TODO using mixer-call + SHOW_CALLBACK( "amc: audioMasterGetInputLatency\n" ); + return( 0 ); + + case audioMasterGetOutputLatency: + // TODO using mixer-call + SHOW_CALLBACK( "amc: audioMasterGetOutputLatency\n" ); + return( 0 ); + + case audioMasterGetPreviousPlug: + SHOW_CALLBACK( "amc: audioMasterGetPreviousPlug\n" ); + // input pin in (-1: first to come), returns + // cEffect* + return( 0 ); + + case audioMasterGetNextPlug: + SHOW_CALLBACK( "amc: audioMasterGetNextPlug\n" ); + // output pin in (-1: first to come), returns + // cEffect* + return( 0 ); + + case audioMasterWillReplaceOrAccumulate: + SHOW_CALLBACK( "amc: audioMasterWillReplaceOr" + "Accumulate\n" ); + // returns: 0: not supported, 1: replace, 2: accumulate + return( 0 ); + + case audioMasterGetCurrentProcessLevel: + SHOW_CALLBACK( "amc: audioMasterGetCurrentProcess" + "Level\n" ); + // returns: 0: not supported, + // 1: currently in user thread (gui) + // 2: currently in audio thread (where process is + // called) + // 3: currently in 'sequencer' thread (midi, timer etc) + // 4: currently offline processing and thus in user + // thread + // other: not defined, but probably pre-empting user + // thread. + return( 0 ); + + case audioMasterGetAutomationState: + SHOW_CALLBACK( "amc: audioMasterGetAutomationState\n" ); + // returns 0: not supported, 1: off, 2:read, 3:write, + // 4:read/write offline + return( 0 ); + + case audioMasterOfflineStart: + SHOW_CALLBACK( "amc: audioMasterOfflineStart\n" ); + return( 0 ); + + case audioMasterOfflineRead: + SHOW_CALLBACK( "amc: audioMasterOfflineRead\n" ); + // ptr points to offline structure, see below. + // return 0: error, 1 ok + return( 0 ); + + case audioMasterOfflineWrite: + SHOW_CALLBACK( "amc: audioMasterOfflineWrite\n" ); + // same as read + return( 0 ); + + case audioMasterOfflineGetCurrentPass: + SHOW_CALLBACK( "amc: audioMasterOfflineGetCurrent" + "Pass\n" ); + return( 0 ); + + case audioMasterOfflineGetCurrentMetaPass: + SHOW_CALLBACK( "amc: audioMasterOfflineGetCurrentMeta" + "Pass\n"); + return( 0 ); + + case audioMasterSetOutputSampleRate: + SHOW_CALLBACK( "amc: audioMasterSetOutputSample" + "Rate\n" ); + // for variable i/o, sample rate in + return( 0 ); + + case audioMasterGetSpeakerArrangement: + SHOW_CALLBACK( "amc: audioMasterGetSpeaker" + "Arrangement\n" ); + // (long)input in , output in + return( 0 ); + + case audioMasterGetVendorString: + SHOW_CALLBACK( "amc: audioMasterGetVendorString\n" ); + // fills with a string identifying the vendor + // (max 64 char) + strcpy( (char *) _ptr, "LAD"); + return( 0 ); + + case audioMasterGetProductString: + SHOW_CALLBACK( "amc: audioMasterGetProductString\n" ); + // fills with a string with product name + // (max 64 char) + strcpy( (char *) _ptr, "VeSTige" ); + return( 0 ); + + case audioMasterGetVendorVersion: + SHOW_CALLBACK( "amc: audioMasterGetVendorVersion\n" ); + // TODO + // returns vendor-specific version + return( 1000 ); + + case audioMasterVendorSpecific: + SHOW_CALLBACK( "amc: audioMasterVendorSpecific\n" ); + // no definition, vendor specific handling + return( 0 ); + + case audioMasterSetIcon: + SHOW_CALLBACK( "amc: audioMasterSetIcon\n" ); + // TODO + // void* in , format not defined yet + return( 0 ); + + case audioMasterCanDo: + SHOW_CALLBACK( "amc: audioMasterCanDo\n" ); + // string in ptr, see below + return( 0 ); + + case audioMasterGetLanguage: + SHOW_CALLBACK( "amc: audioMasterGetLanguage\n" ); + // TODO + // see enum + return( 0 ); + + case audioMasterOpenWindow: + SHOW_CALLBACK( "amc: audioMasterOpenWindow\n" ); + // TODO + // returns platform specific ptr + return( 0 ); + + case audioMasterCloseWindow: + SHOW_CALLBACK( "amc: audioMasterCloseWindow\n" ); + // TODO + // close window, platform specific handle in + return( 0 ); + + case audioMasterGetDirectory: + SHOW_CALLBACK( "amc: audioMasterGetDirectory\n" ); + // TODO + // get plug directory, FSSpec on MAC, else char* + return( 0 ); + + case audioMasterUpdateDisplay: + SHOW_CALLBACK( "amc: audioMasterUpdateDisplay\n" ); + // something has changed, update 'multi-fx' display + _effect->dispatcher( _effect, effEditIdle, 0, 0, NULL, + 0.0f ); + return( 0 ); + + case audioMasterBeginEdit: + SHOW_CALLBACK( "amc: audioMasterBeginEdit\n" ); + // begin of automation session (when mouse down), + // parameter index in + return( 0 ); + + case audioMasterEndEdit: + SHOW_CALLBACK( "amc: audioMasterEndEdit\n" ); + // end of automation session (when mouse up), + // parameter index in + return( 0 ); + + case audioMasterOpenFileSelector: + SHOW_CALLBACK( "amc: audioMasterOpenFileSelector\n" ); + // open a fileselector window with VstFileSelect* + // in + return( 0 ); + + default: + SHOW_CALLBACK( "VST master dispatcher: undefed: " + "%d, %d\n", (int) _opcode, + effKeysRequired ); + break; + } + + return( 0 ); +} + + + +extern "C" +{ + +// neccessary for getting instance out of shared lib +plugin * lmms_plugin_main( void * _data ) +{ + return( new vestigeInstrument( static_cast( _data ) ) ); +} + + +} + + +#include "vestige.moc" + diff --git a/plugins/vestige/vestige.h b/plugins/vestige/vestige.h new file mode 100644 index 000000000..4560c9b00 --- /dev/null +++ b/plugins/vestige/vestige.h @@ -0,0 +1,110 @@ +/* + * vestige.h - instrument VeSTige for hosting VST-plugins + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _VESTIGE_H +#define _VESTIGE_H + +#include "instrument.h" +#include "midi.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include +#include + +#include "spc_bg_hndl_widget.h" + + +class pixmapButton; +class QPixmap; + + +class vestigeInstrument : public instrument, public specialBgHandlingWidget +{ + Q_OBJECT +public: + vestigeInstrument( channelTrack * _channel_track ); + virtual ~vestigeInstrument(); + + virtual void play( void ); + + virtual void FASTCALL playNote( notePlayHandle * _n ); + virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + virtual QString nodeName( void ) const; + + virtual void FASTCALL setParameter( const QString & _param, + const QString & _value ); + + +protected slots: + void openPlugin( void ); + + +protected: + virtual void paintEvent( QPaintEvent * _pe ); + + +private: + void closePlugin( void ); + + void enqueueEvent( const midiEvent & _e, Uint32 _frames_ahead = 0 ); + + static long hostCallback( AEffect *, long, long, long, void *, float ); + + + static bool s_initialized; + static bool s_threadAdopted; + static QPixmap * s_artwork; + + + FSTHandle * m_handle; + FST * m_fst; + + vvector m_midiEvents; + + + pixmapButton * m_openPluginButton; + + QString m_plugin; + + +} ; + + +#endif diff --git a/resources/add_sample_track.png b/resources/add_sample_track.png index 9643357a2f2d3c0bdba7ca7e988573e9aa56fa55..5f00cdd0e099a4424dd1750635229fd44c127a76 100644 GIT binary patch delta 1937 zcmV;C2X6SQ1-cKAKYt1n3Nst^qVDDZ00&x0L_t(|+KpCCuVqycU8`!JbMAfpn*Jiv z1R_KU5YT8FMZgb8G%+&qg8{}kR}*m1CPoMN2b?%@q9#p30wWXS1P3O@kN8Ol5eWoh zq($kbp}Xz(-o5vnvv<{UaGyA}vPV_Bc6Qa?wbtUp4}VE1Uw`&(HJTA!7vlss5II*a zh8)KX*>Y&>k)+&mnyu@yRkhSmax)3Q(6!ZQ#Z#$TG4wIcQEQyziB=zO$AzvJ^-#3ZR*t*v&=9{f+e< zLNbsBke$OVn}1nfkSU3Mr$`oulxFA)B|1ynj7W%zaUzL6Vl%>BfFin^Zn3MYdik29 z+>9dO7UOJvm#t+BMz+-ig37iY*_F)9S}ppVYSvn;6iHT!bq*LyQCHvwFx>oxlUNS* z{6+Jk&S(a6Ww0~SNmy5`Zy;FQ`oiLn6Jl4CA!Gpo34anmQaCh%0AM={2qXakl(=;R z1PDNY00@LZ07!-iL6RU9)qs>O)M9(IY9b*bl_*rPtU;4%$P#I4jf84bYj9JopsBQM zVL)Wm&BTa#J;pp2U-pn}=%f^AeE|VC5rE8Gi{5tz(g=Y_nj6^_bZ)J~P%;yMI*Y}#561VMu4JGMw(gpHcKFiu@piu(4B4uDw1w=W7>8!(%hps z$$xDipr%NJ+DxS_drcLclY|?xpf4P@U|?*i3>LFe*m?e(|zp7|3t&4e~i9-dOn)dY=mwCZ=C zIRhuV(pt1-(eAQMvlT~Q{Nz!(+3r#h4}UfpLg&_Z7|QPF@1I|PYxC>B3}Dub&&-S<9gzI*)7*8A-?z;;oj-*kF$cU7`_qIFcTGe}Iaxvss(HD^HIxKr^W`A^! zqdR%^ATrfEhthxOws`H*{I?4nt5Q6+!(+?1cCh=%gDAFm-~&0%vA+@^CqMV-*C6=L z(8ekE*9c{C+g;j;BN)2++M7InWbwfJCFTAaq>4*A`04NB>V8g5N4ra>FMRN>_n92Y z47~Wtt0zDDsm1jt+GftoGM|4_DSzKtwDpDw>r2~A=-u{@mD_KQg9;#j&y5E^`R(RU zFH1tPwznpUsby;ym#_cuu^-%b`#p2a0E(AOS1y0-D-YeYd{5RItrn9cF_IyITl{50 z`g2Hy%Iz-Cut9+zaQdvj=Z|@TrHM0)M5;*K(Y) zwNe&?GC6dLq={We0L9Xn|9IPmB2weTR9Wmb#vhGGR-ZY>Lf{VgfW*?k;k!@%CO^TeZ~$2{)v0!UkTXxETkzFSMP5|j{i{1rB+Ay>bogx7PX|&JY`B@NjzI5fK zf3E%s;MDTeJvZG$Ck)H71XOaGl_?^oW=19Q9mevh+p-UWk+vL!Bu(ygl)k%lyhAO{Uw0lrA|nGrDghjc#edE1bmSbvTw*cvp_2pOq00K)%L_t(|+T~U;XcJ)+{{DA=HBm!jQ>3+o zf;B}5LKhF)LG7Xh7dyBK_CRnc#ir6hO@t1D3Qp=!g%mQ>QFL$-LUjTH-yy}GHkT%s%c74Pe%|}v`@Z+RcgHadN;J}_jDIiI-(UboG009*S$GRh zVk!b*R8>7tLw_i`+gD%B+20Aw|5JlFOhQq zM*PXph2CzpV1E<9zD!YKoc&3c@{MfRPG)YBFxS1ThL734wtrESX6zt}O!axqWJf$`l5I zo4WzY?GV=zv!22CU*;zU9s%&|-Z%DgEH)Pd5Nrxgo|(+8=Ov(Lv(q1Rn`^4)@AX6< z5?eyy-EiFP$6Eo3;zsen#epJIrD&FWqTH_TEx-yP0gM9>5QZ=yA*28xxGG$?9QstS z{rajNIS~mT4}-zQU0+we8)BDf-Rk{)U+(aXgo0iMf+PsMPG(cNn;?HF{0kr{+g$0( R4>14$002ovPDHLkV1hTBFZ%!h diff --git a/resources/project_export.png b/resources/project_export.png index e23861bf7086f527d38bdd29bc5e9e874d636d5f..3fc6f401cc3f89f90bcad78a41319b6e50844c8f 100644 GIT binary patch literal 1528 zcmV`B}^gX3l!5*r4>lrxIww%qE|LQ00~7%`2h%tpV3HMq*uL(LIufOkwOtt zgd4Tsv=t&k8AI%u#*U5SIbVBmF>{79AGU{fNn87T?7h}|-u13^wgH%#nYmuAR&RQq zS8TOfpEMeczZ8qb?*J4E1^L;HpD(=n>bFaAlBjbjY3fMRv^x?~=TN64X@W~r;y6ZC z-#_8?A$AOp7-V#kN>u! zs=lfkV+^W_@B5X5gM)wU@9%%;0oE8ZAtD%K5E0)PS`!7;{k zf_7)1>)6BdJPLlHGYXyI6+8-_hqZQS7-N{6oaFlT>ntuVGCe(ws$z`67;_TvyewFL z)1VnKSkIy;qJkJhw#qq0#P+Svm=F8PGCO_xG#4&hz&Y1Le(&BrT3>$IC%n`-lBCm=vK+^8LXyP%`A>f& zj$^*~{4)--jEOz~s!9+9EG#VaQMI|b$#3r7?Gseiibq{%GiYB5Mx}U&0aYao!=7sM z&;Q4O8yPS^=SN1nwRVUJFYnwK!}j(z)>_V;J2#ZCyqE^>^UnvbjWINvO&W~`K@jx4 z_XZb+VaSaeH@J4~8cC9j%v}^I&1OWo?Bn}}r1ol`E6B837t3a~ggZVnl8jXPO&0&d^lq|pfZ zI@YIwQ8^xFz|m|*oI2&vY(|8k8!J#g#t;Mf@)~3ziX0moZKBBawcGyruytbs$d_1t zQ5dGIt?hE_*1uR=+oj#kMA`4C-A-9s+vC=)f3vo>OBkkOff`Cd9zYnTeEjhq_4+oo zT8Vmnn=n+u&`~b?1cCH`Y;1INarNpc>h)~^YPAyU>-#)-u*1sA+|dBC6y!C~Y{ooz zu*3556t!B3N+saE_cj4oSvkw(WPx(Iz}8mGHqUdS6-F~8~Z;T5d2rNrWCpmvU z7%8|Bv?oy%eP*o+^8ggkYPI?!KwrFg@qbZxoZ#|SC=^aqtJR+ZrqO7;3#bRAtyb$% zsZ@F_BHqiFFS&E)4(sddz2@|F%@{+Y(V*38WryB)XJ_XTkY=izGsb)?qyDQ^!}wrS eH3U8fp8Xf#OKY^S3wPWA000065Le) literal 1000 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 M07*qoM6N<$f>S7x>Hq)$ literal 1144 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 P*1)#SyTc!ZvP=F4CwH>m delta 1000 zcmVDq~tCVkAy&1Lul6#I{1Fara~m8z<84{zUo zqyP{=5EnBtZM-te9;=~qcfV$TNV)WW9)n7;$k(5L7(M~r`tbu$;vc5pB?TE6e*gXV z6T}4wAb3#R`vjq1y+^411vDhyRg^)D$AZDHkfH0-=LZaTUjQ|KXJBAv2D%#PTP7w( z22lL}272QMP|x4r5WN5a1dqIr-y!(hF9wD`K-c{J5AoiqhYSoFDq;+OA9lJk`~r%< ze8<4>&A?is_52U1uP4=00> zxEI4wphe$*KrBTD_6iIPf?S9o0tg_u!KZ;5KD~X$(7ECj!~gYCSi}4Dc?Je~HK3Qj zL+twX<3GdC?|&J%`B@l$CU1Q7)lfr@8x&z6wEzJGH~7kPpi7|Pdkkm^GtfyOum6SO z-$4A~El?5|abLdywF7m2c>R~*-SeLe(&{V>oZ@oae4IcB{{wrE0U&_j2LJj4icx5I z1HH-ulm?|%PaDzVs z3j6s3!v6LfV(@RM!M~vfv$Jr3P32?*Dgqk93Unbq46ThEbJgN00IbZ z@E2g}`}GUL{`4J^;z2?12O122fIj-kEX#0i{~d-mPws%z?VCqG8D8E0&Y)y2z;Ngq z14DmY_S+XP9_$BynE?<$C@%i}6A=_3ga06fFDy8igjE=X1XLLQvjW4K=^F#{`;QF# zT9ORkKLQ=};$AIK=Pr;L00D&J;=fQAzXe4O)Wx9m40iP&h`}&E!(T|62gSqJ-QbrJxCrFxgh#KGpfOV009Iy`1_Z?4FA6ZRs49( z@aQc>3}o=X{}B2Qln)CYCSVkO`pV4k?I$zCYhV)m^WoJ$O6n|qFJ2>5MTgm WagrK!`2iyMGkW1qD6wdBC)J7&gitg|i&!M5kS3?J zrKHK3*|V1yXJ$I*OKHogzOb3^^}h42ckdYin4FwkXf~TmQ54nN?e?`+tMzNWUVjsy zQmN?Yi(ky1KK)uF@!mp6Nz*`@rrGeMAz;DqsVAfc4=6Tq3XXL;+)J0x+8PgA`2 z-H~|GkaBHpjUTW40A`3P=imQ;7p6}TMbWt%8^3>IW>qs25y8x;R;wrX_xJzY+uOSm z0Zv3FR230HRjVRWH8XMuf>R~92oNy93=08OWozp%zPq%Hs^XmE^*81?G4moK64h$8 z==kyDBuT>V?k;9Vywva&)5fh0)= z>PLl?t}P6Ms^DBV3TA?8S3odBLO}bT6IGlls*<@B2qBP%a6rJnIUoWKu&kgNgy3)) zd9MILMGB=LBk@XOCXkt7K>u5U65xf4AC%#2#C#_a5Dk5#vC-{#xp z<(@$0OYyM%d<)jof?+KlaKOyybUH<9%jX|C5QY+#x8;%B?wsqV>k4=j9^vlYyEx~V znVA{rS2;}m$K~_=`y#^b?k=rXi(0MLb3N+6vD4|WxVXst{5;1J8=QpF{r(2g)4J+<|@(J%5u=vd%v(iV)tsdGojZ z{r#P(sj2foZ5(9Y`_8Rfx31Rf_2=_*MF!$H-a0rq*fcZ$XM-EOZn8jT87jUGOH$i<5nSzli-z7J0e z5uw#;(Qdc%pWfu&y?bjwnx}4BL|!H9{Wxbs_D|3Ot^?bD10$ie0Y0ot%m4rY07*qo IM6N<$g3)am&Hw-a literal 820 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 B8h-!) diff --git a/resources/sample_track.png b/resources/sample_track.png index c8eeee88194ee2197a7ceeb37442063a5635f1f9..0f3837c269f0624f5aaa25fa62f319626a55e61d 100644 GIT binary patch delta 1795 zcmV+e2mJVt0+|kwDt`+T000XU0RWnu7ytkO2XskIMF-Ui6ACg2Xa}Bo000KLNklye%+l;1|ksz3K0~Mb|MNA2hJQQ1k|74z~AH0e;|mHWGo^k zh@hi@1`>nhJDu+L?!D)ny{ndk^E8`1s@kN_=L~FUMa05|W z=V7j8sgxb(ab8Hut=7$2tI;(|bJS%OfVqv+V&SDy@0h1tw(LFVMv7)MhY@c3BtUZu zmj{(FnmJ4`(h5$<0l-}5fM{mnbc29$8fm0k_%x|xQ?pStG6E8e0%&HBuItcvbFrx* zB?D;y*&J>)%zvgpl_XA0Sqz6%W|)E!oiQ#83ZimZNn*;pEN~Z~%=Y3}?a8Ts`czU~ z7LjnvWwWW(7&VevEll|lswNRR-M!hhW$2mp3wE22_Ru$&xw-<&3%HEl+ zmaRq)l&VAtfND?OT}fcb)1rA20Ewj{WtBY#)R%2+or>&TfR&;4z)}`fQdKGJFj4YD zt16P50w97SZ4P(SVe;#v^~#6BssdSS2T^_W)1{~+c${#v@Nh0b z+o)xe8I08jU$}quHQxL9*=uj&GE5lD>ggrr)=ki|<>>yc$B*G;i!pK>L)XW# zZWfLy{Q3KIv+JWE-d-w&<~B9Vas9I&Y=7^4eEH))=EpDDqx<>(XVyX_4$YrUivocHm^_RY}^{&aQiif$DjwwL0H5?~gHZ;fmt9o*qRhpc0On={f zDc|{Md;5JZT_rxe;=^M;y}ka$j}UfvDaK{RuFyFXptd`l9-# z-K`I{mK1grAEghTwy=cX*0*ct(Zm9hT)iGR%@Qp?Ix z940|9C&3mLu1rmBX9UclFnK0S@o z3JB)4ZJet5sF55w&MN?Bx}&`vs0m3BWI{<$LCbc2?Fx$kByl%#2WA1a zV>uuSga}B2>5gMVBT-V6MgoM}PNIS^5@TrVTB@#Q^^_4P^^_Y%VkCy4mk5qjPXJJ= zr^5FI;l~*zJ2q9q$kqhF3838VFuA}io9B_DQzT&4g6@^cxtT-&bAR(Hh70i6IE6t9 zMy;EwMC3Zms6zdpGPe$6tvxegR?7-AxYe>j%Du=^EgJx;YT5P!a@$O*rDyg~79>=m zN+3(hqCtR4Ri!HI^j9JZBqUX#ikT{}DgqQL%iO98qcCaTHvH-Ul^y0gQ8R^82qTd0 zcAS!6mK|rINRvAqoO~L4Xe1632|+eb4q3QCnjBMu5uK6eb=OF>tP+`(=M~{3a_yk< zveuA*Ja05;myTmk(JQcXH0SKVTms1(E!p=}z+kfRQBa l3kN_EFq)A@;0v$7{{jcvQ#@P&gDn66002ovPDHLkV1hTSU2OmW delta 237 zcmV|w+ zsK=B9x6S~1eBZFn)836n3e*tz>(&A)p1)f(=!`dTj<-eNjP~neA~{j+kVIWH=jz=# n00000NkvXXu0mjfoA_rB diff --git a/src/audio/audio_alsa.cpp b/src/audio/audio_alsa.cpp index 83e3a6c41..f5a5f5732 100644 --- a/src/audio/audio_alsa.cpp +++ b/src/audio/audio_alsa.cpp @@ -175,13 +175,13 @@ int audioALSA::handleError( int _err ) void audioALSA::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ) + float _master_gain ) { outputSampleType * outbuf = bufferAllocator::alloc( _frames * channels() ); bufferAllocator::autoCleaner<> ac( outbuf ); - convertToS16( _ab, _frames, _master_output, outbuf, + convertToS16( _ab, _frames, _master_gain, outbuf, m_littleEndian != isLittleEndian() ); Uint32 frame = 0; diff --git a/src/audio/audio_device.cpp b/src/audio/audio_device.cpp index 2a1769d3c..18ce18ad2 100644 --- a/src/audio/audio_device.cpp +++ b/src/audio/audio_device.cpp @@ -70,7 +70,7 @@ audioDevice::~audioDevice() void audioDevice::writeBuffer( surroundSampleFrame * _ab, Uint32 _frames, - Uint32 _src_sample_rate, float _master_output ) + Uint32 _src_sample_rate, float _master_gain ) { // make sure, no other thread is accessing device lock(); @@ -82,12 +82,12 @@ void audioDevice::writeBuffer( surroundSampleFrame * _ab, Uint32 _frames, _frames * channels() ); resample( _ab, _frames, temp, _src_sample_rate, m_sampleRate ); writeBufferToDev( temp, _frames * m_sampleRate / - _src_sample_rate, _master_output ); + _src_sample_rate, _master_gain ); bufferAllocator::free( temp ); } else { - writeBufferToDev( _ab, _frames, _master_output ); + writeBufferToDev( _ab, _frames, _master_gain ); } // release lock unlock(); @@ -240,7 +240,7 @@ LP_FILTER_COEFFS[tap] * lp_hist[( oldest + tap ) % LP_FILTER_TAPS][chnl]; int FASTCALL audioDevice::convertToS16( surroundSampleFrame * _ab, - Uint32 _frames, float _master_output, + Uint32 _frames, float _master_gain, outputSampleType * _output_buffer, bool _convert_endian ) { @@ -251,7 +251,7 @@ int FASTCALL audioDevice::convertToS16( surroundSampleFrame * _ab, ( _output_buffer + frame * channels() )[chnl] = static_cast( mixer::clip( _ab[frame][chnl] * - _master_output ) * + _master_gain ) * OUTPUT_SAMPLE_MULTIPLIER ); } } diff --git a/src/audio/audio_file_ogg.cpp b/src/audio/audio_file_ogg.cpp index c8704309a..27d4177f8 100644 --- a/src/audio/audio_file_ogg.cpp +++ b/src/audio/audio_file_ogg.cpp @@ -186,7 +186,7 @@ bool audioFileOgg::startEncoding( void ) void FASTCALL audioFileOgg::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ) + float _master_gain ) { int eos = 0; @@ -197,7 +197,7 @@ void FASTCALL audioFileOgg::writeBufferToDev( surroundSampleFrame * _ab, { for( Uint8 chnl = 0; chnl < channels(); ++chnl ) { - buffer[chnl][frame] = _ab[frame][chnl] * _master_output; + buffer[chnl][frame] = _ab[frame][chnl] * _master_gain; } } diff --git a/src/audio/audio_file_wave.cpp b/src/audio/audio_file_wave.cpp index 89bebb546..2da1a90b3 100644 --- a/src/audio/audio_file_wave.cpp +++ b/src/audio/audio_file_wave.cpp @@ -88,11 +88,11 @@ bool audioFileWave::startEncoding( void ) void FASTCALL audioFileWave::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ) + float _master_gain ) { outputSampleType * outbuf = bufferAllocator::alloc( _frames * channels() ); - int bytes = convertToS16( _ab, _frames, _master_output, outbuf, + int bytes = convertToS16( _ab, _frames, _master_gain, outbuf, !isLittleEndian() ); writeData( outbuf, bytes ); diff --git a/src/audio/audio_jack.cpp b/src/audio/audio_jack.cpp index a0619f25c..59706fcc4 100644 --- a/src/audio/audio_jack.cpp +++ b/src/audio/audio_jack.cpp @@ -214,7 +214,7 @@ audioJACK::~audioJACK() void audioJACK::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ) + float _master_gain ) { m_bufMutex.lock(); @@ -228,7 +228,7 @@ void audioJACK::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, _frames ); for( Uint32 frame = 0; frame < _frames; ++frame ) { - buf[frame] = _ab[frame][chnl] * _master_output; + buf[frame] = _ab[frame][chnl] * _master_gain; } bufset b = { buf, _frames } ; bufs.push_back( b ); @@ -246,7 +246,10 @@ void audioJACK::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, #ifdef HAVE_UNISTD_H #ifdef HAVE_USLEEP // just wait and give cpu-time to other processes - usleep( 200 ); + // tobydox 20051019: causes LMMS to hang up when locking + // several other mutexes, so skip it + //usleep( 200 ); + #endif #endif } @@ -264,6 +267,8 @@ int audioJACK::processCallback( jack_nframes_t _nframes, void * _udata ) #endif jack_transport_state_t ts = jack_transport_query( _this->m_client, NULL ); + _this->m_bufMutex.lock(); + if( ts != JackTransportRolling ) { // always decrease frame-sync-var as we would do it if running @@ -276,6 +281,7 @@ int audioJACK::processCallback( jack_nframes_t _nframes, void * _udata ) { _this->m_frameSync = 0; } + _this->m_bufMutex.unlock(); return( 0 ); } @@ -289,7 +295,6 @@ int audioJACK::processCallback( jack_nframes_t _nframes, void * _udata ) _this->m_outputPorts[ch], _nframes ); } - _this->m_bufMutex.lock(); jack_nframes_t done = 0; while( done < _nframes ) diff --git a/src/audio/audio_oss.cpp b/src/audio/audio_oss.cpp index 717674c0a..9e7ba5de0 100644 --- a/src/audio/audio_oss.cpp +++ b/src/audio/audio_oss.cpp @@ -278,11 +278,11 @@ QString audioOSS::probeDevice( void ) void audioOSS::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ) + float _master_gain ) { outputSampleType * outbuf = bufferAllocator::alloc( _frames * channels() ); - int bytes = convertToS16( _ab, _frames, _master_output, outbuf, + int bytes = convertToS16( _ab, _frames, _master_gain, outbuf, m_convertEndian ); write( m_audioFD, outbuf, bytes ); diff --git a/src/audio/audio_sdl.cpp b/src/audio/audio_sdl.cpp index 0d4e9f0ef..15b8481c6 100644 --- a/src/audio/audio_sdl.cpp +++ b/src/audio/audio_sdl.cpp @@ -125,10 +125,10 @@ audioSDL::~audioSDL() void audioSDL::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, - float _master_output ) + float _master_gain ) { m_bufMutex.lock(); - convertToS16( _ab, _frames, _master_output, m_buffer, + convertToS16( _ab, _frames, _master_gain, m_buffer, m_convertEndian ); m_bufMutex.unlock(); // before returning make sure, callback was called, so we're synced diff --git a/src/core/bb_editor.cpp b/src/core/bb_editor.cpp index ba5d0c46e..24264ebcc 100644 --- a/src/core/bb_editor.cpp +++ b/src/core/bb_editor.cpp @@ -294,14 +294,6 @@ void bbEditor::keyPressEvent( QKeyEvent * _ke ) -void bbEditor::wheelEvent( QWheelEvent * _we ) -{ - lmmsMainWin::inst()->workspace()->wheelEvent( _we ); -} - - - - void bbEditor::resizeEvent( QResizeEvent * _re ) { updateBackground(); diff --git a/src/core/config_mgr.cpp b/src/core/config_mgr.cpp index 3ada40fe4..59a210bcc 100644 --- a/src/core/config_mgr.cpp +++ b/src/core/config_mgr.cpp @@ -163,10 +163,19 @@ configManager::configManager( void ) : m_lmmsDataDir( qApp->applicationDirPath().section( '/', 0, -2 ) + "/share/lmms/" ), #else - // hardcode since qt 3.0 doesn't know something like - // applicationDirPath and implmeneting own function would be senseless - // since real support for qt 3.0 is senseless too ;-) + // hardcode since qt < 3.2 doesn't know something like + // applicationDirPath and implementing own function would be senseless + // since real support for qt < 3.2 is senseless too ;-) m_lmmsDataDir( "/usr/share/lmms" ), +#endif +#if QT_VERSION >= 0x030200 + m_lmmsPluginDir( qApp->applicationDirPath().section( '/', 0, -2 ) + + "/lib/lmms/" ), +#else + // hardcode since qt < 3.2 doesn't know something like + // applicationDirPath and implementing own function would be senseless + // since real support for qt < 3.2 is senseless too ;-) + m_lmmsPluginDir( "/usr/lib/lmms" ), #endif m_currentPage( 0 ) { diff --git a/src/core/envelope_tab_widget.cpp b/src/core/envelope_tab_widget.cpp index 9a7c9b177..87c7f8e0f 100644 --- a/src/core/envelope_tab_widget.cpp +++ b/src/core/envelope_tab_widget.cpp @@ -153,10 +153,7 @@ envelopeTabWidget::envelopeTabWidget( channelTrack * _channel_track, m_filterComboBox->addItem( tr( "Notch" ) ); m_filterComboBox->addItem( tr( "Allpass" ) ); m_filterComboBox->addItem( tr( "Moog" ) ); - m_filterComboBox->addItem( tr( "Moog 2" ) ); m_filterComboBox->addItem( tr( "2x LowPass" ) ); - m_filterComboBox->addItem( tr( "2x Moog" ) ); - m_filterComboBox->addItem( tr( "2x Moog 2" ) ); #ifdef QT4 m_filterComboBox->setWhatsThis( diff --git a/src/core/instrument.cpp b/src/core/instrument.cpp new file mode 100644 index 000000000..302f3a0a9 --- /dev/null +++ b/src/core/instrument.cpp @@ -0,0 +1,93 @@ +/* + * instrument.cpp - base-class for all instrument-plugins (synths, samplers etc) + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "instrument.h" +#include "channel_track.h" +#include "dummy_instrument.h" + + +instrument::instrument( channelTrack * _channel_track, const QString & _name ) : + QWidget( _channel_track->pluginParent() ), + plugin( _name, INSTRUMENT ), + m_channelTrack( _channel_track ), + m_valid( TRUE ) +{ + setFixedSize( 250, 250 ); +} + + + + +instrument::~instrument() +{ +} + + + + +void instrument::play( void ) +{ +} + + + + +void instrument::playNote( notePlayHandle * ) +{ +} + + + + +void instrument::deleteNotePluginData( notePlayHandle * ) +{ +} + + + + +Uint32 instrument::beatLen( notePlayHandle * ) const +{ + return( 0 ); +} + + + + +instrument * instrument::instantiate( const QString & _plugin_name, + channelTrack * _channel_track ) +{ + plugin * p = plugin::instantiate( _plugin_name, _channel_track ); + // check whether instantiated plugin is an instrument + if( dynamic_cast( p ) != NULL ) + { + // everything ok, so return pointer + return( dynamic_cast( p ) ); + } + + // not quite... so delete plugin and return dummy instrument + delete p; + return( new dummyInstrument( _channel_track ) ); +} + diff --git a/src/core/main.cpp b/src/core/main.cpp index f34bca727..2431adcbf 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -163,6 +163,7 @@ int main( int argc, char * * argv ) e->show(); e->exportBtnClicked(); } + return( app.exec() ); } diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index a81bdcaea..77f6c905e 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -211,27 +211,6 @@ void mixer::run( void ) // while we're acting... m_safetySyncMutex.lock(); -/* following code is faster but unstable since using iterators - while deleting from vector is dangerous and often leads to - undefined results... - - playHandleVector::iterator it = m_playHandles.begin(); - while( it != m_playHandles.end() ) - { - if( ( *it )->done() ) - { - // delete all play-handles which have - // played completely now - delete *it; - m_playHandles.erase( it ); - } - else - { - // play all uncompletely-played play-handles... - ( *it )->play(); - ++it; - } - }*/ csize idx = 0; while( idx < m_playHandles.size() ) { diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp new file mode 100644 index 000000000..cfc1b2d4c --- /dev/null +++ b/src/core/plugin.cpp @@ -0,0 +1,175 @@ +/* + * plugin.cpp - implemenation of plugin-class including plugin-loader + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_DLFCN_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "plugin.h" +#include "mixer.h" +#include "config_mgr.h" +#include "dummy_plugin.h" + + + +plugin::plugin( const QString & _public_name, pluginTypes _type ) : + settings(), + m_publicName( _public_name ), + m_type( _type ) +{ +} + + + + +plugin::~plugin() +{ +} + + + + +void plugin::setParameter( const QString &, const QString & ) +{ +} + + + + +QString plugin::getParameter( const QString & ) +{ + return( "" ); +} + + + + +plugin * plugin::instantiate( const QString & _plugin_name, void * _data ) +{ +#ifdef HAVE_DLFCN_H + void * handle = dlopen( QString( "lib" + _plugin_name + ".so" ). +#ifdef QT4 + toAscii().constData() +#else + ascii() +#endif + , RTLD_LAZY ); + if( handle == NULL ) + { + QMessageBox::information( NULL, + mixer::tr( "Plugin not found" ), + mixer::tr( "The %1-plugin wasn't found!" + ).arg( _plugin_name ), + QMessageBox::Ok | + QMessageBox::Default ); + return( new dummyPlugin() ); + } + dlerror(); + instantiationHook inst_hook = ( instantiationHook )( dlsym( handle, + "lmms_plugin_main" ) ); + char * error = dlerror(); + if( error != NULL ) + { + printf( "%s\n", error ); + QMessageBox::information( NULL, + mixer::tr( "Error while loading plugin" ), + mixer::tr( "Failed loading plugin \"%1\"!" + ).arg( _plugin_name ), + QMessageBox::Ok | + QMessageBox::Default ); + return( new dummyPlugin() ); + } + plugin * inst = inst_hook( _data ); + //dlclose( handle ); + return( inst ); +#endif + return( new dummyPlugin() ); +} + + + + +void plugin::getDescriptorsOfAvailPlugins( vvector & _plugin_descs ) +{ + QDir directory( configManager::inst()->pluginDir() ); + const QFileInfoList * list = directory.entryInfoList( "*.so" ); + if( list == NULL ) + { + return; + } + for( QFileInfoList::iterator file = list->begin(); + file != list->end(); ++file ) + { + void * handle = dlopen( ( *file )->absFilePath().latin1(), + RTLD_LAZY ); + char * msg = dlerror(); + if( msg != NULL || handle == NULL ) + { + printf( "plugin-loader: %s\n", msg ); + continue; + } + void * foo = dlsym( handle, "lmms_plugin_main" ); + msg = dlerror(); + if( msg != NULL || foo == NULL ) + { + printf( "plugin-loader: %s\n", msg ); + continue; + } + QString desc_name = ( *file )->fileName().mid( 3 ).section( + '.', 0, 0 ) + "_plugin_descriptor"; + descriptor * plugin_desc = + (descriptor *) dlsym( handle, desc_name.ascii() ); + msg = dlerror(); + if( msg != NULL || plugin_desc == NULL ) + { + printf( "plugin-loader: %s\n", msg ); + continue; + } + _plugin_descs.push_back( *plugin_desc ); + //dlclose( handle ); + } + +} + + + diff --git a/src/core/plugin_browser.cpp b/src/core/plugin_browser.cpp new file mode 100644 index 000000000..75ebc169e --- /dev/null +++ b/src/core/plugin_browser.cpp @@ -0,0 +1,248 @@ +/* + * plugin_browser.cpp - implementation of the plugin-browser + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +/*#include */ +#include + +#else + +#include +#include +/*#include */ +#include + +#endif + + +#include "plugin_browser.h" +#include "embed.h" +#include "debug.h" +#include "gui_templates.h" +#include "string_pair_drag.h" + + + +pluginBrowser::pluginBrowser( QWidget * _parent ) : + sideBarWidget( tr( "Instrument plugins" ), + embed::getIconPixmap( "plugins" ), _parent ) +{ + setWindowTitle( tr( "Plugin browser" ) ); + m_view = new QWidget( contentParent() ); + //m_view->setFrameShape( QFrame::NoFrame ); + + addContentWidget( m_view ); + + QVBoxLayout * view_layout = new QVBoxLayout( m_view, 5, 10 ); + + + QLabel * hint = new QLabel( tr( "You can drag an instrument-plugin " + "into either the Song-Editor, the " + "Beat+Bassline Editor or just into a " + "channel-window or on the " + "corresponding channel-button." ), + m_view ); + hint->setFont( pointSize<8>( hint->font() ) ); + hint->setAlignment( hint->alignment() | WordBreak ); + view_layout->addWidget( hint ); + + plugin::getDescriptorsOfAvailPlugins( m_pluginDescriptors ); + + for( vvector::iterator it = + m_pluginDescriptors.begin(); + it != m_pluginDescriptors.end(); ++it ) + { + pluginDescWidget * p = new pluginDescWidget( *it, m_view ); + p->show(); + view_layout->addWidget( p ); + } + view_layout->addStretch(); + show(); +} + + + + +pluginBrowser::~pluginBrowser() +{ +} + + + + + + + +pluginDescWidget::pluginDescWidget( const plugin::descriptor & _pd, + QWidget * _parent ) : + QWidget( _parent ), + m_pluginDescriptor( _pd ), + m_logo(), + m_mouseOver( FALSE ) +{ + m_logo.loadFromData( _pd.logo.data, _pd.logo.size ); + setFixedHeight( 60 ); + setMouseTracking( TRUE ); +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + setCursor( PointingHandCursor ); +} + + + + +pluginDescWidget::~pluginDescWidget() +{ +} + + + + +void pluginDescWidget::paintEvent( QPaintEvent * ) +{ + QColor fill_color( 192, 192, 192 ); + if( m_mouseOver ) + { + fill_color = QColor( 224, 224, 224 ); + } + +#ifdef QT4 + QPainter p( this ); + p.fillRect( rect(), fill_color ); +#else + // create pixmap for whole widget + QPixmap pm( rect().size() ); + pm.fill( fill_color ); + + // and a painter for it + QPainter p( &pm ); +#endif + p.setPen( QColor( 64, 64, 64 ) ); + p.drawRect( rect() ); + p.drawPixmap( 4, 4, m_logo ); + + QFont f = pointSize<8>( p.font() ); + f.setBold( TRUE ); + p.setFont( f ); + p.drawText( 58, 14, m_pluginDescriptor.public_name ); + + f.setBold( FALSE ); + p.setFont( pointSize<7>( f ) ); + QStringList words = QStringList::split( ' ', + plugin::tr( m_pluginDescriptor.description ) ); + for( QStringList::iterator it = words.begin(); it != words.end(); ++it ) + { + if( ( *it ).contains( '-' ) ) + { + QStringList splitted_word = QStringList::split( '-', + *it ); + QStringList::iterator orig_it = it; + for( QStringList::iterator it2 = splitted_word.begin(); + it2 != splitted_word.end(); ++it2 ) + { + if( it2 == splitted_word.fromLast() ) + { + words.insert( it, *it2 ); + } + else + { + words.insert( it, *it2 + "-" ); + ++it; + } + } + words.erase( orig_it ); + --it; + } + } + + int y = 26; + int avail_width = width() - 58 - 5; + QString s; + for( QStringList::iterator it = words.begin(); it != words.end(); ++it ) + { + if( p.fontMetrics().width( s + *it + " " ) >= avail_width ) + { + p.drawText( 58, y, s ); + y += 10; + s = ""; + } + s += *it; + if( ( *it ).right( 1 ) != "-" ) + { + s += " "; + } + } + p.drawText( 58, y, s ); + +#ifndef QT4 + // blit drawn pixmap to actual widget + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void pluginDescWidget::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton ) + { + new stringPairDrag( "plugin", m_pluginDescriptor.name, m_logo, + this ); + m_mouseOver = FALSE; + update(); + } +} + + + + +void pluginDescWidget::mouseMoveEvent( QMouseEvent * _me ) +{ + bool new_mouse_over = rect().contains( _me->pos() ); + if( new_mouse_over != m_mouseOver ) + { + m_mouseOver = new_mouse_over; + update(); + } +} + + + + +void pluginDescWidget::mouseReleaseEvent( QMouseEvent * _me ) +{ + mouseMoveEvent( _me ); +} + + + +#include "plugin_browser.moc" + diff --git a/src/core/preset_preview_play_handle.cpp b/src/core/preset_preview_play_handle.cpp index 9cc4f2eab..0829aa7e9 100644 --- a/src/core/preset_preview_play_handle.cpp +++ b/src/core/preset_preview_play_handle.cpp @@ -115,7 +115,7 @@ presetPreviewPlayHandle::presetPreviewPlayHandle( multimediaProject mmp( _preset_file ); if( s_globalChannelTrack == NULL ) { - track * t = track::createTrack( track::CHANNEL_TRACK, + track * t = track::create( track::CHANNEL_TRACK, blindTrackContainer::inst() ); s_globalChannelTrack = dynamic_cast( t ); #ifdef LMMS_DEBUG diff --git a/src/core/song_editor.cpp b/src/core/song_editor.cpp index 289d57ce2..4673e4e83 100644 --- a/src/core/song_editor.cpp +++ b/src/core/song_editor.cpp @@ -351,16 +351,6 @@ songEditor::songEditor() : /* edit_tb->setPaletteBackgroundPixmap( embed::getIconPixmap( "toolbar_bg" ) ); edit_tb->setErasePixmap( embed::getIconPixmap( "toolbar_bg" ) );*/ -#ifdef QT4 - a = edit_tb->addAction( embed::getIconPixmap( "add_channel_track" ), "", - this, SLOT( addChannelTrack() ) ); - a->setToolTip( tr( "Add channel-track" ) ); -#else - m_addChannelTrackButton = new QToolButton( embed::getIconPixmap( - "add_channel_track" ), "", "", - this, SLOT( addChannelTrack() ), - edit_tb ); -#endif #ifdef QT4 a = edit_tb->addAction( embed::getIconPixmap( "add_bb_track" ), "", this, SLOT( addBBTrack() ) ); @@ -414,7 +404,6 @@ songEditor::songEditor() : toolTip::add( m_playButton, tr( "Play/pause song (Space)" ) ); toolTip::add( m_stopButton, tr( "Stop playing song (Space)" ) ); - toolTip::add( m_addChannelTrackButton, tr( "Add channel-track" ) ); toolTip::add( m_addBBTrackButton, tr( "Add beat/bassline" ) ); toolTip::add( m_addSampleTrackButton, tr( "Add sample-track" ) ); toolTip::add( m_insertTactButton, tr( "Insert tact at current tact " @@ -609,7 +598,6 @@ void songEditor::scrolled( int _new_pos ) void songEditor::wheelEvent( QWheelEvent * _we ) { - _we->accept(); if( m_controlPressed ) { if( _we->delta() > 0 ) @@ -636,6 +624,12 @@ void songEditor::wheelEvent( QWheelEvent * _we ) m_leftRightScroll->setValue( m_leftRightScroll->value() - _we->delta() / 30 ); } + else + { + _we->ignore(); + return; + } + _we->accept(); } @@ -1290,24 +1284,9 @@ void songEditor::removeTact( void ) -void songEditor::addChannelTrack( void ) -{ - channelTrack * t = dynamic_cast< channelTrack * >( - track::createTrack( track::CHANNEL_TRACK, this ) ); -#ifdef LMMS_DEBUG - assert( t != NULL ); -#endif - t->loadPlugin( "tripleoscillator" ); - t->toggledChannelButton( TRUE ); - t->show(); -} - - - - void songEditor::addBBTrack( void ) { - track * t = track::createTrack( track::BB_TRACK, this ); + track * t = track::create( track::BB_TRACK, this ); if( dynamic_cast( t ) != NULL ) { dynamic_cast( t )->clickedTrackLabel(); @@ -1319,7 +1298,7 @@ void songEditor::addBBTrack( void ) void songEditor::addSampleTrack( void ) { - (void) track::createTrack( track::SAMPLE_TRACK, this ); + (void) track::create( track::SAMPLE_TRACK, this ); } @@ -1435,14 +1414,14 @@ void songEditor::createNewProject( void ) clearProject(); track * t; - t = track::createTrack( track::CHANNEL_TRACK, this ); - dynamic_cast< channelTrack * >( t )->loadPlugin( + t = track::create( track::CHANNEL_TRACK, this ); + dynamic_cast< channelTrack * >( t )->loadInstrument( "tripleoscillator" ); - track::createTrack( track::SAMPLE_TRACK, this ); - t = track::createTrack( track::CHANNEL_TRACK, bbEditor::inst() ); - dynamic_cast< channelTrack * >( t )->loadPlugin( + track::create( track::SAMPLE_TRACK, this ); + t = track::create( track::CHANNEL_TRACK, bbEditor::inst() ); + dynamic_cast< channelTrack * >( t )->loadInstrument( "tripleoscillator" ); - track::createTrack( track::BB_TRACK, this ); + track::create( track::BB_TRACK, this ); setBPM( DEFAULT_BPM ); m_masterVolumeSlider->setValue( 100 ); diff --git a/src/core/track.cpp b/src/core/track.cpp index 674288ac6..ebbc48e52 100644 --- a/src/core/track.cpp +++ b/src/core/track.cpp @@ -607,10 +607,10 @@ trackWidget::trackWidget( track * _track, QWidget * _parent ) : "", &m_trackOperationsWidget ); deltr_btn->setGeometry( 1, 1+TRACK_OP_BTN_HEIGHT, TRACK_OP_BTN_WIDTH, TRACK_OP_BTN_HEIGHT ); - connect( deltr_btn, SIGNAL( clicked() ), this, SLOT( deleteTrack() ) ); + connect( deltr_btn, SIGNAL( clicked() ), this, SLOT( removeTrack() ) ); connect( deltr_btn, SIGNAL( clicked() ), deltr_btn, SLOT( clearFocus() ) ); - toolTip::add( deltr_btn, tr( "Delete this track" ) ); + toolTip::add( deltr_btn, tr( "Remove this track" ) ); QPushButton * muptr_btn = new QPushButton( embed::getIconPixmap( "arp_up_on", 12, 12 ), @@ -718,7 +718,7 @@ void trackWidget::cloneTrack( void ) -void trackWidget::deleteTrack( void ) +void trackWidget::removeTrack( void ) { m_track->getTrackContainer()->removeTrack( m_track ); } @@ -873,7 +873,7 @@ track::~track() -track * FASTCALL track::createTrack( trackTypes _tt, trackContainer * _tc ) +track * FASTCALL track::create( trackTypes _tt, trackContainer * _tc ) { switch( _tt ) { @@ -891,10 +891,10 @@ track * FASTCALL track::createTrack( trackTypes _tt, trackContainer * _tc ) -track * FASTCALL track::createTrack( const QDomElement & _this, +track * FASTCALL track::create( const QDomElement & _this, trackContainer * _tc ) { - track * t = createTrack( static_cast( _this.attribute( + track * t = create( static_cast( _this.attribute( "type" ).toInt() ), _tc ); #ifdef LMMS_DEBUG assert( t != NULL ); @@ -906,13 +906,13 @@ track * FASTCALL track::createTrack( const QDomElement & _this, -track * FASTCALL track::cloneTrack( track * _track ) +track * FASTCALL track::clone( track * _track ) { QDomDocument doc; QDomElement parent = doc.createElement( "clone" ); _track->saveSettings( doc, parent ); QDomElement e = parent.firstChild().toElement(); - return( createTrack( e, _track->getTrackContainer() ) ); + return( create( e, _track->getTrackContainer() ) ); } diff --git a/src/core/track_container.cpp b/src/core/track_container.cpp index 25627f27c..6c191d907 100644 --- a/src/core/track_container.cpp +++ b/src/core/track_container.cpp @@ -51,7 +51,9 @@ #include "lmms_main_win.h" #include "mixer.h" #include "song_editor.h" - +#include "string_pair_drag.h" +#include "channel_track.h" +#include "mmp.h" @@ -69,15 +71,9 @@ trackContainer::trackContainer() : lmmsMainWin::inst()->workspace()->addWindow( this ); #endif - m_scrollArea = new QScrollArea( this ); - m_scrollArea->setFrameStyle( QFrame::NoFrame ); - m_scrollArea->setHorizontalScrollBarPolicy( -#ifdef QT4 - Qt::ScrollBarAlwaysOff -#else - QScrollArea::AlwaysOff -#endif - ); + m_scrollArea = new scrollArea( this ); + + setAcceptDrops( TRUE ); } @@ -157,7 +153,7 @@ void trackContainer::loadSettings( const QDomElement & _this ) if( node.isElement() ) { - track::createTrack( node.toElement(), this ); + track::create( node.toElement(), this ); } node = node.nextSibling(); } @@ -176,7 +172,7 @@ void trackContainer::loadSettings( const QDomElement & _this ) void trackContainer::cloneTrack( track * _track ) { - track::cloneTrack( _track ); + track::clone( _track ); } @@ -372,6 +368,41 @@ void trackContainer::resizeEvent( QResizeEvent * ) +void trackContainer::dragEnterEvent( QDragEnterEvent * _dee ) +{ + stringPairDrag::processDragEnterEvent( _dee, "preset,plugin" ); +} + + + + +void trackContainer::dropEvent( QDropEvent * _de ) +{ + QString type = stringPairDrag::decodeKey( _de ); + QString value = stringPairDrag::decodeValue( _de ); + if( type == "plugin" ) + { + channelTrack * ct = dynamic_cast( + track::create( track::CHANNEL_TRACK, + this ) ); + ct->loadInstrument( value ); + ct->toggledChannelButton( TRUE ); + } + else if( type == "preset" ) + { + multimediaProject mmp( value ); + channelTrack * ct = dynamic_cast( + track::create( track::CHANNEL_TRACK, + this ) ); + ct->loadTrackSpecificSettings( mmp.content().firstChild(). + toElement() ); + ct->toggledChannelButton( TRUE ); + } +} + + + + void trackContainer::updateScrollArea( void ) { m_scrollArea->resize( tMax( m_scrollArea->parentWidget()->width() - @@ -384,6 +415,45 @@ void trackContainer::updateScrollArea( void ) +trackContainer::scrollArea::scrollArea( trackContainer * _parent ) : + QScrollArea( _parent ) +{ + setFrameStyle( QFrame::NoFrame ); + setHorizontalScrollBarPolicy( +#ifdef QT4 + Qt::ScrollBarAlwaysOff +#else + QScrollArea::AlwaysOff +#endif + ); +} + + + + +trackContainer::scrollArea::~scrollArea() +{ +} + + + + +void trackContainer::scrollArea::wheelEvent( QWheelEvent * _we ) +{ + // always pass wheel-event to parent-widget (song-editor + // bb-editor etc.) because they might want to use it for zooming + // or scrolling left/right if a modifier-key is pressed, otherwise + // they do not accept it and we pass it up to QScrollArea + dynamic_cast( parentWidget() )->wheelEvent( _we ); + if( !_we->isAccepted() ) + { + QScrollArea::wheelEvent( _we ); + } +} + + + + #include "track_container.moc" #undef setValue diff --git a/src/lib/buffer_allocator.cpp b/src/lib/buffer_allocator.cpp index f49391937..6e5cde08d 100644 --- a/src/lib/buffer_allocator.cpp +++ b/src/lib/buffer_allocator.cpp @@ -188,7 +188,7 @@ void * bufferAllocator::allocBytes( Uint32 _bytes ) } - // nothing so far, so we'll alloc a new (aligned) buf + // got nothing so far, so we'll alloc a new (aligned) buf bufDesc d = { FALSE, new char[_bytes + BUFFER_ALIGN], NULL, _bytes, 0 }; d.buf = (void *)( (size_t) d.origPtr + ( BUFFER_ALIGN - ( (size_t) d.origPtr & diff --git a/src/lib/embed.cpp b/src/lib/embed.cpp index 9bd9934bb..ae2d5c336 100644 --- a/src/lib/embed.cpp +++ b/src/lib/embed.cpp @@ -43,9 +43,15 @@ #include "config_mgr.h" + +#ifndef PLUGIN_NAME namespace embed +#else +namespace PLUGIN_NAME +#endif { + #include "embedded_resources.h" @@ -55,10 +61,10 @@ QPixmap getIconPixmap( const char * _name, int _w, int _h ) { QString name = QString( _name ) + ".png"; #ifdef QT4 - const embedDesc & e = findEmbeddedData( + const embed::descriptor & e = findEmbeddedData( name.toAscii().constData() ); #else - const embedDesc & e = findEmbeddedData( name.ascii() ); + const embed::descriptor & e = findEmbeddedData( name.ascii() ); #endif // not found? if( QString( e.name ) != name ) @@ -88,13 +94,12 @@ QPixmap getIconPixmap( const char * _name, int _w, int _h ) QString getText( const char * _name ) { - const embedDesc & e = findEmbeddedData( _name ); + const embed::descriptor & e = findEmbeddedData( _name ); return( QString::fromLatin1( (const char *) e.data, e.size ) ); } - void loadTranslation( const QString & _tname ) { QTranslator * t = new QTranslator( 0 ); @@ -103,9 +108,10 @@ void loadTranslation( const QString & _tname ) #if QT_VERSION >= 0x030100 #ifdef QT4 - const embedDesc & e = findEmbeddedData( name.toAscii().constData() ); + const embed::descriptor & e = findEmbeddedData( + name.toAscii().constData() ); #else - const embedDesc & e = findEmbeddedData( name.ascii() ); + const embed::descriptor & e = findEmbeddedData( name.ascii() ); #endif // not found? if( QString( e.name ) != name ) @@ -126,3 +132,4 @@ void loadTranslation( const QString & _tname ) } + diff --git a/src/lib/ladspa_manager.cpp b/src/lib/ladspa_manager.cpp index 8d8d0afa0..88bdba73d 100644 --- a/src/lib/ladspa_manager.cpp +++ b/src/lib/ladspa_manager.cpp @@ -23,26 +23,31 @@ */ +#include "ladspa_manager.h" + +#ifdef LADSPA_SUPPORT + + #ifdef QT4 #include #include -#include #else #include #include -#include #endif -#include + +#ifdef HAVE_DLFCN_H #include +#endif + #include -#include "ladspa_manager.h" ladspaManager * ladspaManager::s_instanceOfMe = NULL; @@ -55,19 +60,25 @@ ladspaManager::ladspaManager( void ) // TODO Need to move the search path definition to the config file to have // more control over where it tries to find the plugins. - QStringList ladspaDirectories = QStringList::split( QString( ":" ), + QStringList ladspaDirectories = QStringList::split( ':', QString( getenv( "LADSPA_PATH" ) ) ); // set default-directory if nothing is specified... if( ladspaDirectories.isEmpty() ) { ladspaDirectories.push_back( "/usr/lib/ladspa" ); + ladspaDirectories.push_back( "/usr/local/lib/ladspa" ); } for( QStringList::iterator it = ladspaDirectories.begin(); it != ladspaDirectories.end(); ++it ) { QDir directory( ( *it ) ); const QFileInfoList * list = directory.entryInfoList(); - + // if directory doesn't exist or isn't readable, we get NULL which + // would crash LMMS... + if( list == NULL ) + { + continue; + } for( QFileInfoList::iterator file = list->begin(); file != list->end(); ++file ) { @@ -842,3 +853,6 @@ void FASTCALL ladspaManager::cleanup( const ladspaKey & _plugin, } } + +#endif + diff --git a/src/lib/string_pair_drag.cpp b/src/lib/string_pair_drag.cpp new file mode 100644 index 000000000..aecc8d292 --- /dev/null +++ b/src/lib/string_pair_drag.cpp @@ -0,0 +1,112 @@ +/* + * string_pair_drag.cpp - class stringPairDrag which provides general support + * for drag'n'drop of string-pairs + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "string_pair_drag.h" + + + +stringPairDrag::stringPairDrag( const QString & _key, const QString & _value, + const QPixmap & _icon, QWidget * _w ) : +#ifdef QT4 + QDrag( _w ) +#else + QStoredDrag( "lmms/stringpair", _w ) +#endif +{ + setPixmap( _icon ); + QString txt = _key + ":" + _value; +#ifdef QT4 + QMimeData * m = new QMimeData(); + m->setData( "lmms/stringpair", txt.toAscii() ); + setMimeData( m ); + start( /*Qt::CopyAction*/ Qt::IgnoreAction ); +#else + setEncodedData( txt.utf8() ); + drag(); +#endif +} + + + + +stringPairDrag::~stringPairDrag() +{ + // TODO: do we have to delete anything??? +} + + + + +void stringPairDrag::processDragEnterEvent( QDragEnterEvent * _dee, + const QString & _allowed_keys ) +{ +#ifdef QT4 + if( !_dee->mimeData()->hasFormat( "lmms/stringpair" ) ) + { + return; + } + QString txt = _dee->mimeData()->data(); +#else + QString txt = _dee->encodedData( "lmms/stringpair" ); +#endif + bool accepted = QStringList::split( ',', _allowed_keys ).contains( + txt.section( ':', 0, 0 ) ); +#ifdef QT4 + if( accepted ) + { + _dee->acceptProposedAction(); + } +#else + _dee->accept( accepted ); +#endif + +} + + + + +QString stringPairDrag::decodeKey( QDropEvent * _de ) +{ +#ifdef QT4 + return( QString( _de->mimeData()->data() ).section( ':', 0, 0 ) ); +#else + return( QString( _de->encodedData( "lmms/stringpair" ) ).section( + ':', 0, 0 ) ); +#endif +} + + + + +QString stringPairDrag::decodeValue( QDropEvent * _de ) +{ +#ifdef QT4 + return( QString( _de->mimeData()->data() ).section( ':', 1, 1 ) ); +#else + return( QString( _de->encodedData( "lmms/stringpair" ) ).section( + ':', 1, 1 ) ); +#endif +} + diff --git a/src/widgets/pixmap_button.cpp b/src/widgets/pixmap_button.cpp index f2a6e9abc..11c604607 100644 --- a/src/widgets/pixmap_button.cpp +++ b/src/widgets/pixmap_button.cpp @@ -29,6 +29,7 @@ #include #include + #else #include @@ -87,9 +88,9 @@ void pixmapButton::paintEvent( QPaintEvent * ) } #ifdef QT4 - if( isChecked() ) + if( isChecked() || isDown() ) #else - if( isOn() ) + if( isOn() || isDown() ) #endif { if( m_activePixmap != NULL )