From 40d8b3c4f6f2eaf073fe588206bd3364ebb829a4 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 24 Oct 2005 09:13:39 +0000 Subject: [PATCH] completely new MIDI-subsystem and other bugfixes, see ChangeLog for details git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@20 0778d3d1-df1d-0410-868b-ea421aaaa00d --- ChangeLog | 62 ++ Makefile.am | 24 +- README | 3 +- TODO | 8 +- configure.in | 75 +- include/arp_and_chords_tab_widget.h | 7 +- include/envelope_tab_widget.h | 6 +- include/file_browser.h | 205 +++++ include/instrument.h | 122 +++ include/instrument_play_handle.h | 71 ++ include/knob.h | 22 +- include/lcd_spinbox.h | 15 + include/led_checkbox.h | 1 + include/midi.h | 11 +- include/midi_alsa_raw.h | 14 +- include/midi_client.h | 153 ++++ include/midi_dummy.h | 13 +- include/midi_event_processor.h | 59 ++ include/midi_oss.h | 14 +- include/midi_port.h | 132 +++ include/midi_tab_widget.h | 96 +++ include/midi_time.h | 13 +- include/mixer.h | 18 +- include/piano_widget.h | 19 +- include/setup_dialog.h | 4 +- include/song_editor.h | 2 +- include/track_container.h | 3 + plugins/Makefile.am | 7 +- plugins/audio_file_processor/Makefile.am | 1 - .../audio_file_processor.cpp | 10 +- .../plucked_string_synth.cpp | 2 +- .../triple_oscillator/triple_oscillator.cpp | 2 +- plugins/vestige/vestige.cpp | 2 +- src/core/arp_and_chords_tab_widget.cpp | 48 +- src/core/envelope_tab_widget.cpp | 10 +- src/core/file_browser.cpp | 756 ++++++++++++++++++ src/core/instrument.cpp | 2 +- src/core/midi_tab_widget.cpp | 196 +++++ src/core/mixer.cpp | 25 +- src/core/piano_roll.cpp | 40 +- src/core/piano_widget.cpp | 59 +- src/core/plugin_browser.cpp | 2 +- src/core/setup_dialog.cpp | 6 +- src/core/song_editor.cpp | 2 +- src/core/track_container.cpp | 5 +- src/midi/midi_alsa_raw.cpp | 16 +- src/midi/midi_client.cpp | 339 ++++++++ src/midi/midi_oss.cpp | 18 +- src/midi/midi_port.cpp | 85 ++ src/widgets/knob.cpp | 213 +++-- src/widgets/lcd_spinbox.cpp | 45 +- src/widgets/led_checkbox.cpp | 4 + src/widgets/tempo_sync_knob.cpp | 59 +- 53 files changed, 2739 insertions(+), 387 deletions(-) create mode 100644 include/file_browser.h create mode 100644 include/instrument.h create mode 100644 include/instrument_play_handle.h create mode 100644 include/midi_client.h create mode 100644 include/midi_event_processor.h create mode 100644 include/midi_port.h create mode 100644 include/midi_tab_widget.h create mode 100644 src/core/file_browser.cpp create mode 100644 src/core/midi_tab_widget.cpp create mode 100644 src/midi/midi_client.cpp create mode 100644 src/midi/midi_port.cpp diff --git a/ChangeLog b/ChangeLog index eaebadb61..1c17326ff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,65 @@ +2005-10-23 Tobias Doerffel + + * src/widgets/led_checkbox.cpp: + also emit toggled()-signal if state actually wasn't changed + + * include/lcd_spinbox.h: + * src/widgets/lcd_spinbox.cpp: + - display special strings if value is a certain number + - support for disabled-state (gray/no input) + + * include/midi_tab_widget.h: + * src/core/midi_tab_widget.cpp: + * src/tracks/channel_track.cpp: + added new tab "MIDI" for being able to setup MIDI-related stuff for + each channel + + * include/channel_track.h: + * include/midi*: + * include/piano_widget.h: + * include/setup_dialog.h: + * src/core/mixer.cpp: + * src/core/piano_roll.cpp: + * src/core/piano_widget.cpp: + * src/core/setup_dialog.cpp: + * src/core/song_editor.cpp: + * src/midi/midi*: + * src/tracks/channel_track.cpp: + coded a completely new, powerful and clean MIDI-system which e.g. makes + it possible to mask MIDI-events for each channel and to receive and send + (timed!) MIDI-events on a separate MIDI-port for each channel, which + only makes sense if using non-raw (sequenced) MIDI-client - currently + none existing, but ALSA-sequencer-support is in progress + + * include/midi_device.h: + * src/midi/midi_device.cpp: + removed + +2005-10-21 Tobias Doerffel + + * src/widgets/tempo_sync_knob.cpp: + do not implement the same code as knob does in mouseMoveEvent() - call + knob::mouseMoveEvent() instead + + * include/knob.h: + * src/widgets/knob.cpp: + cleaned up a lot and fixed some bugs + +2005-10-20 Tobias Doerffel + + * include/song_editor.h: + decreased MAX_BPM to 999 since BPM-LCD-spinbox is intended to have + only three digits while 1000 has four of them... ;-) + + * include/track_container.h: + * src/core/track_container.cpp: + trackContainer::scrollArea-class has now m_trackContainer-member + for storing parent which makes cast of parent-widget to track-container + (which sometimes failed...) obsolete + + * configure.in: + check for libfst and present VST-SDK header-files + 2005-10-19 Tobias Doerffel * plugins/vestige/vestige.h: diff --git a/Makefile.am b/Makefile.am index 736133edf..e6385cfcc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -62,6 +62,7 @@ lmms_MOC = \ ./mixer.moc \ ./name_label.moc \ ./nstate_button.moc \ + ./midi_tab_widget.moc \ ./pattern.moc \ ./piano_roll.moc \ ./piano_widget.moc \ @@ -117,6 +118,7 @@ lmms_SOURCES = \ $(srcdir)/src/core/instrument.cpp \ $(srcdir)/src/core/lmms_main_win.cpp \ $(srcdir)/src/core/main.cpp \ + $(srcdir)/src/core/midi_tab_widget.cpp \ $(srcdir)/src/core/mixer.cpp \ $(srcdir)/src/core/name_label.cpp \ $(srcdir)/src/core/note.cpp \ @@ -142,10 +144,11 @@ lmms_SOURCES = \ $(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_client.cpp \ $(srcdir)/src/midi/midi_file.cpp \ $(srcdir)/src/midi/midi_mapper.cpp \ $(srcdir)/src/midi/midi_oss.cpp \ + $(srcdir)/src/midi/midi_port.cpp \ $(srcdir)/src/tracks/bb_track.cpp \ $(srcdir)/src/tracks/channel_track.cpp \ $(srcdir)/src/tracks/pattern.cpp \ @@ -190,7 +193,6 @@ lmms_SOURCES = \ $(srcdir)/include/song_editor.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 \ @@ -227,10 +229,14 @@ lmms_SOURCES = \ $(srcdir)/include/name_label.h \ $(srcdir)/include/play_handle.h \ $(srcdir)/include/mmp.h \ - $(srcdir)/include/midi_device.h \ - $(srcdir)/include/midi_file.h \ + $(srcdir)/include/midi.h \ $(srcdir)/include/midi_alsa_raw.h \ + $(srcdir)/include/midi_client.h \ + $(srcdir)/include/midi_event_processor.h \ + $(srcdir)/include/midi_file.h \ $(srcdir)/include/midi_oss.h \ + $(srcdir)/include/midi_port.h \ + $(srcdir)/include/midi_time.h \ $(srcdir)/include/clipboard.h \ $(srcdir)/include/types.h \ $(srcdir)/include/qt3support.h \ @@ -245,7 +251,6 @@ lmms_SOURCES = \ $(srcdir)/include/endian_handling.h \ $(srcdir)/include/preset_preview_play_handle.h \ $(srcdir)/include/sample_play_handle.h \ - $(srcdir)/include/midi.h \ $(srcdir)/include/nstate_button.h \ $(srcdir)/include/midi_dummy.h \ $(srcdir)/include/midi_mapper.h \ @@ -260,7 +265,8 @@ lmms_SOURCES = \ $(srcdir)/include/dummy_instrument.h \ $(srcdir)/include/instrument_play_handle.h \ $(srcdir)/include/string_pair_drag.h \ - $(srcdir)/include/ladspa_manager.h + $(srcdir)/include/ladspa_manager.h \ + $(srcdir)/include/midi_tab_widget.h EXTRA_DIST = $(lmms_EMBEDDED_RESOURCES) @@ -296,6 +302,10 @@ 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) -lfst -ldl +if HAVE_LIBFST +LIB_FST_LDADD = -lfst +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) $(LIB_FST_LDADD) -ldl lmms_LDFLAGS = -rdynamic -rpath $(pkglibdir) diff --git a/README b/README index 8c6932c87..e9fd1c832 100644 --- a/README +++ b/README @@ -47,7 +47,8 @@ least 500 MHz, but for really enjoying LMMS less than 1 GHz makes no sense... Required libraries are: -- Qt 3.0 (3.2 recommended) or higher (tested up to 4.0.0) with devel-files +- multihreaded version of 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 diff --git a/TODO b/TODO index 75a0d9146..731d05632 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,12 @@ +- arpeggio: send midi-out-events via channel-track +- tooltips for controls in MIDI-tab +- sample-track: sane bg and wave-color - 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 +- DSSI-support - 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 +- save/load parameters of VST-plugin +- somehow avoid hidden plugin-descriptor-widgets plugin-browser 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 diff --git a/configure.in b/configure.in index b2cafdf18..4b1dd307c 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-cvs20051019, tobydox@users.sourceforge.net) -AM_INIT_AUTOMAKE(lmms, 0.1.1-cvs20051019) +AC_INIT(lmms, 0.1.1-cvs20051023, tobydox@users.sourceforge.net) +AM_INIT_AUTOMAKE(lmms, 0.1.1-cvs20051023) AM_CONFIG_HEADER(config.h) @@ -154,6 +154,32 @@ fi AM_CONDITIONAL(HAVE_LIBJACK, test ! -z "$HAVE_JACK_JACK_H") +# check for VST-backends (currently only libfst is supported) +AC_ARG_WITH(vst, + AS_HELP_STRING([--without-vst], + [disable support for VST-plugin-hosting]), , + [ with_vst=yes ]) +AH_TEMPLATE(HAVE_VST_AEFFECTX_H, [Define to 1 if you have the header file.]) +AH_TEMPLATE(HAVE_FST_H, [Define to 1 if you have the header file.]) +if test "x$with_vst" = "xyes" ; then + AC_CHECK_HEADER(vst/aeffectx.h, HAVE_VST_AEFFECTX_H="true") + AC_CHECK_HEADER(fst.h, HAVE_FST_H="true") + AC_CHECK_LIB([fst], [fst_init], HAVE_LIBFST="true", HAVE_FST_H="") +fi +if test ! -z "$HAVE_VST_AEFFECTX_H" ; then + AC_DEFINE(HAVE_VST_AEFFECTX_H) + if test ! -z "$HAVE_FST_H" ; then + AC_DEFINE(HAVE_FST_H) + FST_OK_BUT_VST_HEADERS_MISSING="" + else + FST_OK_BUT_VST_HEADERS_MISSING="true" + fi +else + HAVE_FST_H="" +fi +AM_CONDITIONAL(HAVE_LIBFST, test ! -z "$HAVE_FST_H") + + # check for vorbis-lib AC_ARG_WITH(vorbis, AS_HELP_STRING([--without-vorbis], @@ -541,6 +567,49 @@ else fi + +if test ! -z "$FST_OK_BUT_VST_HEADERS_MISSING" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You seem to have a proper libfst-installation, but the header-files" + echo " = (AEffect.h and aeffectx.h) from Steinberg-SDK are missing or not present" + echo " = in /usr/include/vst. We cannot distribute them as they're licensed under" + echo " = a non-GPL-compatible license, so you'll have to download them at" + echo " =" + echo " = ftp://ext2asio:sdk1ext@ftp.pinnaclesys.com/SDK" + echo " =" + echo " = and put the mentioned files into /usr/include." + echo " = Otherwise (now!) configure will disable LMMS's support for built-in VST-" + echo " = plugin-usage. If you do not intend to use VST-plugins with LMMS you can " + echo " = ignore this warning." + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" +else + if test ! -z "$HAVE_FST_H" ; then + PLUGINS_TO_BUILD="$PLUGINS_TO_BUILD\n\t\* libfst for hosting VST-plugins" + else + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have installed libfst, which is neccessary for building" + echo " = LMMS with support for built-in VST-plugin. Furthermore LMMS needs" + echo " = header-files (AEffect.h and aeffectx.h) from Steinberg-SDK, which are" + echo " = likely to be installed by libfst, so after having coped with libfst-" + echo " = installation (which is a mess... ;-), everything should be alright." + echo " = If you do not intend to use VST-plugins with LMMS you can ignore this " + echo " = warning." + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" + fi +fi + + + echo echo echo "LMMS will be able to use $PLUGINS_TO_BUILD" | sed -e "s/\\\n/\n/g" | sed -e "s/\\\t/\t/g" | sed -e "s/\\\\\*/\*/g" @@ -562,7 +631,7 @@ else fi echo " =" echo " = If there're problems while compiling LMMS, please send a mail to " -echo " = tobydox@users.sourceforge.net!" +echo " = tobydox [at] users.sourceforge.net!" echo " =" echo diff --git a/include/arp_and_chords_tab_widget.h b/include/arp_and_chords_tab_widget.h index ef9a7bcd2..fa58cf58c 100644 --- a/include/arp_and_chords_tab_widget.h +++ b/include/arp_and_chords_tab_widget.h @@ -64,9 +64,8 @@ class arpAndChordsTabWidget : public QWidget, public settings { Q_OBJECT public: - arpAndChordsTabWidget( channelTrack * _channel_track, - QWidget * _parent ); - ~arpAndChordsTabWidget(); + arpAndChordsTabWidget( channelTrack * _channel_track ); + virtual ~arpAndChordsTabWidget(); static struct chord { @@ -114,8 +113,6 @@ private: RANDOM } m_arpDirection; - channelTrack * m_channelTrack; - // chord-stuff groupBox * m_chordsGroupBox; QComboBox * m_chordsComboBox; diff --git a/include/envelope_tab_widget.h b/include/envelope_tab_widget.h index d7276b3cf..59d778ec1 100644 --- a/include/envelope_tab_widget.h +++ b/include/envelope_tab_widget.h @@ -59,8 +59,8 @@ class envelopeTabWidget : public QWidget, public settings { Q_OBJECT public: - envelopeTabWidget( channelTrack * _channel_track, QWidget * parent ); - ~envelopeTabWidget(); + envelopeTabWidget( channelTrack * _channel_track ); + virtual ~envelopeTabWidget(); void FASTCALL processAudioBuffer( sampleFrame * _ab, Uint32 _frames, notePlayHandle * _n ); @@ -91,8 +91,6 @@ public: private: - channelTrack * m_channelTrack; - tabWidget * m_targetsTabWidget; envelopeAndLFOWidget * m_envLFOWidgets[TARGET_COUNT]; diff --git a/include/file_browser.h b/include/file_browser.h new file mode 100644 index 000000000..e9ba58b2f --- /dev/null +++ b/include/file_browser.h @@ -0,0 +1,205 @@ +/* + * file_browser.h - include file for fileBrowser + * + * 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 _FILE_BROWSER_H +#define _FILE_BROWSER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include +#include + +#endif + + +#include "side_bar_widget.h" + + + +class fileItem; +class trackContainer; +class QPixmap; +class playHandle; + + +class fileBrowser : public sideBarWidget +{ + Q_OBJECT +public: + fileBrowser( const QString & _path, const QString & _filter, + const QString & _title, const QPixmap & _pm, + QWidget * _parent ); + virtual ~fileBrowser(); + + +public slots: + void reloadTree( void ); + + +protected: + void keyPressEvent( QKeyEvent * _ke ); + + +protected slots: +#ifdef QT4 + void itemPressed( int btn, Q3ListViewItem * _i, const QPoint & _pos, + int _col ); + void itemReleased( int btn, Q3ListViewItem * _i, const QPoint & _pos, + int _col ); + void itemDoubleClicked( Q3ListViewItem * _i, const QPoint & _pos, + int _col ); + void contextMenuRequest( Q3ListViewItem * _i, const QPoint & _pos, + int _col ); +#else + void itemPressed( int btn, QListViewItem * _i, const QPoint & _pos, + int _col ); + void itemReleased( int btn, QListViewItem * _i, const QPoint & _pos, + int _col ); + void itemDoubleClicked( QListViewItem * _i, const QPoint & _pos, + int _col ); + void contextMenuRequest( QListViewItem * _i, const QPoint & _pos, + int _col ); +#endif + void selectionChanged( void ); + void sendToActiveChannel( void ); + void openInNewChannelSE( void ); + void openInNewChannelBBE( void ); + void renameItem( void ); + + +private: + void openInNewChannel( trackContainer * _tc ); + + Q3ListView * m_l; + fileItem * m_contextMenuItem; + + QString m_path; + QString m_filter; + + playHandle * m_previewPlayHandle; + +} ; + + + +class directory : public Q3ListViewItem +{ +public: + directory( Q3ListView * _parent, const QString & _filename, + const QString & _path, const QString & _filter ); + directory( directory * _parent, const QString & _filename, + const QString & _path, const QString & _filter ); + + void setOpen( bool ); + void setup( void ); + + inline QString fullName( void ) + { +#ifdef QT4 + return( QDir::cleanPath( m_path + "/" + text( 0 ) + "/" ) ); +#else + return( QDir::cleanDirPath( m_path + "/" + text( 0 ) + "/" ) ); +#endif + } + + inline const QPixmap * pixmap( int ) const + { + return( m_pix ); + } + + +private: + void initPixmapStuff( void ); + //using Q3ListViewItem::setPixmap; + void FASTCALL setPixmap( QPixmap * _px ); + + static QPixmap * s_folderPixmap; + static QPixmap * s_folderOpenedPixmap; + static QPixmap * s_folderLockedPixmap; + + directory * m_p; + QPixmap * m_pix; + QString m_path; + QString m_filter; + +} ; + + + +class fileItem : public Q3ListViewItem +{ +public: + fileItem( Q3ListView * _parent, const QString & _name, + const QString & _path ); + fileItem( Q3ListViewItem * _parent, const QString & _name, + const QString & _path ); + + inline QString fullName( void ) const + { + return( m_path + "/" + text( 0 ) ); + } + inline const QPixmap * pixmap( int ) const + { + return( m_pix ); + } + + enum fileTypes + { + SONG_FILE, PRESET_FILE, SAMPLE_FILE, UNKNOWN + } ; + + inline fileTypes type( void ) + { + return( m_type ); + } + + +private: + void initPixmapStuff( void ); + void determineFileType( void ); + + static QPixmap * s_songFilePixmap; + static QPixmap * s_presetFilePixmap; + static QPixmap * s_sampleFilePixmap; + static QPixmap * s_unknownFilePixmap; + + QPixmap * m_pix; + QString m_path; + fileTypes m_type; +} ; + + + +#endif diff --git a/include/instrument.h b/include/instrument.h new file mode 100644 index 000000000..6cbc4b3e1 --- /dev/null +++ b/include/instrument.h @@ -0,0 +1,122 @@ +/* + * instrument.h - declaration of class instrument, which provides a + * standard interface for all instrument 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 _INSTRUMENT_H +#define _INSTRUMENT_H + + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "plugin.h" +#include "mixer.h" + + +// forward-declarations +class channelTrack; +class notePlayHandle; + + +class instrument : public QWidget, public plugin +{ +public: + instrument( channelTrack * _channel_track, const QString & _name ); + virtual ~instrument(); + + // if the plugin doesn't play each note, it can create an instrument- + // play-handle and re-implement this method, so that it mixes it's + // output buffer only once per mixer-period + virtual void play( void ); + + // must be overloaded by actual plugin + virtual void FASTCALL playNote( notePlayHandle * note_to_play ); + + // needed for deleting plugin-specific-data of a note - plugin has to + // cast void-ptr so that the plugin-data is deleted properly + // (call of dtor if it's a class etc.) + virtual void FASTCALL deleteNotePluginData( notePlayHandle * + _note_to_play ); + + // Get number of sample-frames that should be used when playing beat + // (note with unspecified length) + // Per default this function returns 0. In this case, channel is using + // the length of the longest envelope (if one active). + virtual Uint32 FASTCALL beatLen( notePlayHandle * _n ) const; + + + // instrument-play-handles use this for checking whether they can mark + // themselves as done, so that mixer trashes them + inline virtual bool valid( void ) const + { + return( m_valid ); + } + + + // instantiate instrument-plugin with given name or return NULL + // on failure + static instrument * FASTCALL instantiate( const QString & _plugin_name, + channelTrack * _channel_track ); + +protected: + inline channelTrack * getChannelTrack( void ) const + { + return( m_channelTrack ); + } + + // instruments can use this for invalidating themselves, which is for + // example neccessary when being destroyed and having instrument-play- + // handles running + inline void invalidate( void ) + { + m_valid = FALSE; + mixer::inst()->checkValidityOfPlayHandles(); + } + + +private: + channelTrack * m_channelTrack; + bool m_valid; + +} ; + + +#endif diff --git a/include/instrument_play_handle.h b/include/instrument_play_handle.h new file mode 100644 index 000000000..bfdf51dd0 --- /dev/null +++ b/include/instrument_play_handle.h @@ -0,0 +1,71 @@ +/* + * instrument_play_handle.h - play-handle for playing an instrument + * + * 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 _INSTRUMENT_PLAY_HANDLE_H +#define _INSTRUMENT_PLAY_HANDLE_H + +#include "play_handle.h" +#include "instrument.h" + + +class instrumentPlayHandle : public playHandle +{ +public: + inline instrumentPlayHandle( instrument * _instrument ) : + playHandle(), + m_instrument( _instrument ) + { + } + + inline virtual ~instrumentPlayHandle() + { + } + + + inline virtual void play( void ) + { + m_instrument->play(); + } + + inline virtual bool done( void ) const + { + return( m_instrument == NULL ); + } + + inline virtual void checkValidity( void ) + { + if( !m_instrument->valid() ) + { + m_instrument = NULL; + } + } + + +private: + instrument * m_instrument; + +} ; + + +#endif diff --git a/include/knob.h b/include/knob.h index f5d387e5c..9d333efd2 100644 --- a/include/knob.h +++ b/include/knob.h @@ -61,16 +61,7 @@ public: knob( int _knob_num, QWidget * _parent, const QString & _name ); virtual ~knob(); - enum ScrollMode - { - ScrNone, - ScrMouse, - ScrTimer, - ScrDirect, - ScrPage - } ; - void setTracking( bool _enable ); void setHintText( const QString & _txt_before, const QString & _txt_after ); void setLabel( const QString & _txt ); @@ -110,6 +101,7 @@ public slots: void reset( void ); void copyValue( void ); void pasteValue( void ); + void enterValue( void ); void connectToMidiDevice( void ); void displayHelp( void ); @@ -127,6 +119,7 @@ protected: virtual void mousePressEvent( QMouseEvent * _me ); virtual void mouseReleaseEvent( QMouseEvent * _me ); virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void mouseDoubleClickEvent( QMouseEvent * _me ); virtual void wheelEvent( QWheelEvent * _me ); virtual void contextMenuEvent( QContextMenuEvent * _me ); @@ -140,8 +133,6 @@ protected: //private: void layoutKnob( bool _update = TRUE ); float getValue( const QPoint & _p ); - void getScrollMode( const QPoint & _p, int & _scroll_mode, - int & _direction ); void recalcAngle( void ); void valueChange( void ); @@ -150,25 +141,20 @@ protected: void buttonReleased( void ); - void setNewValue( float x, int align = 0 ); + void setNewValue( float _x, bool _align = FALSE ); static float s_copiedValue; static textFloat * s_textFloat; - int m_knobWidth; - int m_scrollMode; float m_mouseOffset; - int m_direction; - int m_tracking; QPoint m_origMousePos; + bool m_buttonPressed; float m_angle; - float m_oldAngle; float m_totalAngle; - float m_nTurns; QPixmap * m_knobPixmap; int m_knobNum; diff --git a/include/lcd_spinbox.h b/include/lcd_spinbox.h index 1a92975e0..ac8f330ac 100644 --- a/include/lcd_spinbox.h +++ b/include/lcd_spinbox.h @@ -30,14 +30,18 @@ #ifdef QT4 #include +#include #else #include +#include #endif +class QLabel; + class lcdSpinBox : public QWidget { @@ -56,6 +60,15 @@ public: void setValue( int _value ); void setLabel( const QString & _txt ); + inline void addTextForValue( int _val, const QString & _text ) + { + m_textForValue[_val] = _text; + } + + +public slots: + virtual void setEnabled( bool _on ); + protected: virtual void mousePressEvent( QMouseEvent * _me ); @@ -65,6 +78,8 @@ protected: private: + QMap m_textForValue; + int m_value; int m_minValue; int m_maxValue; diff --git a/include/led_checkbox.h b/include/led_checkbox.h index bb404b429..dba066b29 100644 --- a/include/led_checkbox.h +++ b/include/led_checkbox.h @@ -70,6 +70,7 @@ public slots: void toggle( void ); void setChecked( bool _on ); + protected: virtual void paintEvent( QPaintEvent * _pe ); virtual void mousePressEvent( QMouseEvent * _me ); diff --git a/include/midi.h b/include/midi.h index 5c2ccee31..a5dd6b3bb 100644 --- a/include/midi.h +++ b/include/midi.h @@ -61,11 +61,13 @@ enum midiEventTypes } ; +const Sint8 MIDI_CHANNEL_COUNT = 16; + struct midiEvent { midiEvent( midiEventTypes _type = MIDI_ACTIVE_SENSING, - Uint8 _channel = 0, + Sint8 _channel = 0, Uint16 _param1 = 0, Uint16 _param2 = 0 ) : m_type( _type ), @@ -75,7 +77,8 @@ struct midiEvent m_data.m_param[0] = _param1; m_data.m_param[1] = _param2; } - midiEvent( midiEventTypes _type, char * _sysex_data, int _data_len ) : + midiEvent( midiEventTypes _type, const char * _sysex_data, + int _data_len ) : m_type( _type ), m_channel( 0 ), m_sysExData( _sysex_data ) @@ -92,14 +95,14 @@ struct midiEvent } midiEventTypes m_type; // MIDI event type - Uint8 m_channel; // MIDI channel + Sint8 m_channel; // MIDI channel union { Uint16 m_param[2]; // first/second parameter (key/velocity) int m_sysExDataLen; // len of m_sysExData } m_data; - char * m_sysExData; + const char * m_sysExData; } ; diff --git a/include/midi_alsa_raw.h b/include/midi_alsa_raw.h index 988268df1..2196af779 100644 --- a/include/midi_alsa_raw.h +++ b/include/midi_alsa_raw.h @@ -1,5 +1,5 @@ /* - * midi_alsa_raw.h - midi-device-driver for RawMIDI via ALSA + * midi_alsa_raw.h - midi-client for RawMIDI via ALSA * * Linux MultiMedia Studio * Copyright (c) 2004-2005 Tobias Doerffel @@ -50,17 +50,17 @@ #endif -#include "midi_device.h" +#include "midi_client.h" struct pollfd; class QLineEdit; -class midiALSARaw : public midiDevice, public QThread +class midiALSARaw : public midiRawClient, public QThread { public: - midiALSARaw( channelTrack * _ct = NULL ); + midiALSARaw( void ); ~midiALSARaw(); static QString probeDevice( void ); @@ -73,7 +73,7 @@ public: } - class setupWidget : public midiDevice::setupWidget + class setupWidget : public midiRawClient::setupWidget { public: setupWidget( QWidget * _parent ); @@ -88,8 +88,8 @@ public: protected: - virtual void FASTCALL sendByte( Uint8 _c ); - virtual void FASTCALL run( void ); + virtual void FASTCALL sendByte( const Uint8 _c ); + virtual void run( void ); private: diff --git a/include/midi_client.h b/include/midi_client.h new file mode 100644 index 000000000..3a28d87de --- /dev/null +++ b/include/midi_client.h @@ -0,0 +1,153 @@ +/* + * midi_client.h - base-class for MIDI-clients like ALSA-sequencer-client + * + * 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 _MIDI_CLIENT_H +#define _MIDI_CLIENT_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "midi.h" +#include "midi_event_processor.h" +#include "tab_widget.h" + + +class midiPort; + + +// base-class for all MIDI-clients +class midiClient +{ +public: + midiClient( void ); + virtual ~midiClient(); + + virtual midiPort * FASTCALL createPort( midiEventProcessor * _mep, + const QString & _desired_name ) = 0; + + virtual void FASTCALL processOutEvent( const midiEvent & _me, + const midiTime & _time, + const midiPort * _port ) = 0; + + // validate port-name by trying to change port name in underlying MIDI- + // subsystem + virtual void FASTCALL validatePortName( midiPort * _port ) = 0; + + void FASTCALL removePort( midiPort * _port ); + + static midiClient * openMidiClient( void ); + + + class setupWidget : public tabWidget + { + public: + setupWidget( const QString & _caption, QWidget * _parent ) : + tabWidget( tabWidget::tr( "Settings for %1" ).arg( + _caption ), _parent ) + { + } + + virtual ~setupWidget() + { + } + + virtual void saveSettings( void ) = 0; + + } ; + + +protected: + inline void addPort( midiPort * _port ) + { + m_midiPorts.push_back( _port ); + } + + + vvector m_midiPorts; + +} ; + + + + +const Uint8 RAW_MIDI_PARSE_BUF_SIZE = 16; + + +class midiRawClient : public midiClient +{ +public: + midiRawClient( void ); + ~midiRawClient(); + + +protected: + virtual midiPort * FASTCALL createPort( midiEventProcessor * _mep, + const QString & _desired_name ); + + virtual void FASTCALL validatePortName( midiPort * _port ); + + void FASTCALL parseData( const Uint8 _c ); + + virtual void FASTCALL sendByte( const Uint8 _c ) = 0; + + +private: + void processParsedEvent(); + void FASTCALL processOutEvent( const midiEvent & _me, + const midiTime & _time, + const midiPort * _port ); + + static Uint8 FASTCALL eventLength( const Uint8 _event ); + + + struct midiParserData + { + Uint8 m_status; // identifies the type of event, that + // is currently received ('Noteon', + // 'Pitch Bend' etc). + Uint8 m_channel; // The channel of the event that is + // received (in case of a channel event) + Uint32 m_bytes; // How many bytes have been read for + // the current event? + Uint32 m_bytesTotal; // How many bytes does the current + // event type include? + Uint32 m_buffer[RAW_MIDI_PARSE_BUF_SIZE]; + // buffer for incoming data + midiEvent m_midiEvent; // midi-event + } m_midiParseData; +} ; + + + +#endif diff --git a/include/midi_dummy.h b/include/midi_dummy.h index e8276bc7a..657882f09 100644 --- a/include/midi_dummy.h +++ b/include/midi_dummy.h @@ -26,15 +26,16 @@ #define _MIDI_DUMMY_H -#include "midi_device.h" +#include "midi_client.h" +#include "midi_port.h" #include "tab_widget.h" -class midiDummy : public midiDevice +class midiDummy : public midiRawClient { public: midiDummy() : - midiDevice() + midiRawClient() { } ~midiDummy() @@ -47,11 +48,11 @@ public: } - class setupWidget : public midiDevice::setupWidget + class setupWidget : public midiClient::setupWidget { public: setupWidget( QWidget * _parent ) : - midiDevice::setupWidget( midiDummy::name(), _parent ) + midiRawClient::setupWidget( midiDummy::name(), _parent ) { } @@ -67,7 +68,7 @@ public: protected: - virtual void FASTCALL sendByte( Uint8 ) + virtual void FASTCALL sendByte( const Uint8 ) { } diff --git a/include/midi_event_processor.h b/include/midi_event_processor.h new file mode 100644 index 000000000..da4a72ea5 --- /dev/null +++ b/include/midi_event_processor.h @@ -0,0 +1,59 @@ +/* + * midi_event_processor.h - base-class for midi-processing classes + * + * 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 _MIDI_EVENT_PROCESSOR_H +#define _MIDI_EVENT_PROCESSOR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "midi_time.h" + + +class midiEvent; + + +// all classes being able to process MIDI-events should inherit from this +class midiEventProcessor +{ +public: + inline midiEventProcessor( void ) + { + } + + virtual inline ~midiEventProcessor() + { + } + + // to be implemented by inheriting classes + virtual void FASTCALL processInEvent( const midiEvent & _me, + const midiTime & _time ) = 0; + virtual void FASTCALL processOutEvent( const midiEvent & _me, + const midiTime & _time ) = 0; + +} ; + +#endif diff --git a/include/midi_oss.h b/include/midi_oss.h index d5335d1d1..294f5715c 100644 --- a/include/midi_oss.h +++ b/include/midi_oss.h @@ -1,5 +1,5 @@ /* - * midi_oss.h - OSS-driver for MIDI-port + * midi_oss.h - OSS-raw-midi-client * * Linux MultiMedia Studio * Copyright (c) 2004-2005 Tobias Doerffel @@ -33,16 +33,16 @@ #include #include -#include "midi_device.h" +#include "midi_client.h" class QLineEdit; -class midiOSS : public midiDevice, public QThread +class midiOSS : public midiRawClient, public QThread { public: - midiOSS( channelTrack * _ct = NULL ); + midiOSS( void ); ~midiOSS(); static QString probeDevice( void ); @@ -54,7 +54,7 @@ public: } - class setupWidget : public midiDevice::setupWidget + class setupWidget : public midiRawClient::setupWidget { public: setupWidget( QWidget * _parent ); @@ -69,8 +69,8 @@ public: protected: - virtual void FASTCALL sendByte( Uint8 _c ); - virtual void FASTCALL run( void ); + virtual void FASTCALL sendByte( const Uint8 _c ); + virtual void run( void ); private: diff --git a/include/midi_port.h b/include/midi_port.h new file mode 100644 index 000000000..b5ee66165 --- /dev/null +++ b/include/midi_port.h @@ -0,0 +1,132 @@ +/* + * midi_port.h - abstraction of MIDI-ports which are part of LMMS's MIDI- + * sequencing system + * + * 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 _MIDI_PORT_H +#define _MIDI_PORT_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "types.h" +#include "midi.h" +#include "midi_time.h" + + +class midiClient; +class midiEventProcessor; + + +// class for abstraction of MIDI-port +class midiPort +{ +public: + enum modes + { + DUMMY, // don't route any MIDI-events (default) + INPUT, // from MIDI-client to MIDI-event-processor + OUTPUT, // from MIDI-event-processor to MIDI-client + DUPLEX // both directions + } ; + + midiPort( midiClient * _mc, midiEventProcessor * _mep, + const QString & _name, modes _mode = DUMMY ); + ~midiPort(); + + inline const QString & name( void ) const + { + return( m_name ); + } + + inline void setName( const QString & _name ) + { + m_name = _name; + } + + void FASTCALL trySetName( const QString & _name ); + + inline modes mode( void ) const + { + return( m_mode ); + } + + inline void setMode( modes _mode ) + { + m_mode = _mode; + } + + inline Sint8 inputChannel( void ) const + { + return( m_inputChannel ); + } + + inline void setInputChannel( Sint8 _chnl ) + { + m_inputChannel = _chnl; + } + + inline Sint8 outputChannel( void ) const + { + return( m_outputChannel ); + } + + inline void setOutputChannel( Sint8 _chnl ) + { + m_outputChannel = _chnl; + } + + + void FASTCALL processInEvent( const midiEvent & _me, + const midiTime & _time ); + void FASTCALL processOutEvent( const midiEvent & _me, + const midiTime & _time ); + + +private: + midiClient * m_midiClient; + midiEventProcessor * m_midiEventProcessor; + QString m_name; + modes m_mode; + Sint8 m_inputChannel; + Sint8 m_outputChannel; + +} ; + + + +#endif diff --git a/include/midi_tab_widget.h b/include/midi_tab_widget.h new file mode 100644 index 000000000..b40be5f5a --- /dev/null +++ b/include/midi_tab_widget.h @@ -0,0 +1,96 @@ +/* + * midi_tab_widget.h - tab-widget in channel-track-window for setting up + * MIDI-related stuff + * + * 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 _MIDI_TAB_WIDGET_H +#define _MIDI_TAB_WIDGET_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "settings.h" + + +class QComboBox; +class QPixmap; + +class channelTrack; +class tabWidget; +class ledCheckBox; +class lcdSpinBox; +class midiPort; + + +class midiTabWidget : public QWidget, public settings +{ + Q_OBJECT +public: + midiTabWidget( channelTrack * _channel_track, midiPort * _port ); + ~midiTabWidget(); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "midi" ); + } + + + +protected slots: + void inputChannelChanged( int ); + void outputChannelChanged( int ); + void midiPortModeToggled( bool ); + + +private: + channelTrack * m_channelTrack; + midiPort * m_midiPort; + + tabWidget * m_setupTabWidget; + lcdSpinBox * m_inputChannelSpinBox; + lcdSpinBox * m_outputChannelSpinBox; + ledCheckBox * m_receiveCheckBox; + ledCheckBox * m_sendCheckBox; + ledCheckBox * m_routeCheckBox; + +} ; + + +#endif diff --git a/include/midi_time.h b/include/midi_time.h index 74abfba1f..5fbbd9035 100644 --- a/include/midi_time.h +++ b/include/midi_time.h @@ -32,13 +32,13 @@ class midiTime { public: - inline midiTime( tact _tact, tact64th _tact_64th ) : + inline midiTime( const tact _tact, const tact64th _tact_64th ) : m_tact( _tact ), m_tact64th( _tact_64th ) { } - inline midiTime( Sint32 _abs = 0 ) : + inline midiTime( const Sint32 _abs = 0 ) : m_tact( _abs / 64 ), m_tact64th( _abs % 64 ) { @@ -97,7 +97,7 @@ public: } // calculate number of frame that are needed this time - inline Uint32 frames( float _frames_per_tact ) const + inline Uint32 frames( const float _frames_per_tact ) const { if( m_tact >= 0 ) { @@ -110,6 +110,13 @@ public: return( 0 ); } + static inline midiTime fromFrames( Uint32 _frames, + const float _frames_per_tact ) + { + return( midiTime( static_cast( _frames * 64.0f / + _frames_per_tact ) ) ); + } + private: tact m_tact; diff --git a/include/mixer.h b/include/mixer.h index b6d52dfa1..6333483a2 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -55,7 +55,7 @@ class audioDevice; -class midiDevice; +class midiClient; class lmmsMainWin; class plugin; @@ -153,15 +153,15 @@ public: void restoreAudioDevice( void ); - // MIDI-device-stuff - inline const QString & midiDevName( void ) const + // MIDI-client-stuff + inline const QString & midiClientName( void ) const { - return( m_midiDevName ); + return( m_midiClientName ); } - inline midiDevice * getMIDIDevice( void ) + inline midiClient * getMIDIClient( void ) { - return( m_midiDev ); + return( m_midiClient ); } @@ -289,7 +289,7 @@ private: audioDevice * tryAudioDevices( void ); - midiDevice * tryMIDIDevices( void ); + midiClient * tryMIDIClients( void ); @@ -326,8 +326,8 @@ private: QString m_audioDevName; - midiDevice * m_midiDev; - QString m_midiDevName; + midiClient * m_midiClient; + QString m_midiClientName; QMutex m_safetySyncMutex; diff --git a/include/piano_widget.h b/include/piano_widget.h index fd113aa3c..933fa52f5 100644 --- a/include/piano_widget.h +++ b/include/piano_widget.h @@ -62,18 +62,17 @@ class pianoWidget : public QWidget Q_OBJECT public: pianoWidget( channelTrack * _channel_track ); - ~pianoWidget(); + virtual ~pianoWidget(); protected: - void paintEvent( QPaintEvent * ); - void mousePressEvent( QMouseEvent * me ); - void mouseReleaseEvent( QMouseEvent * me ); - void mouseMoveEvent( QMouseEvent * me ); - void keyPressEvent( QKeyEvent * ke ); - void keyReleaseEvent( QKeyEvent * ke ); - void focusInEvent( QFocusEvent * _fe ); - void focusOutEvent( QFocusEvent * _fe ); + virtual void paintEvent( QPaintEvent * ); + virtual void mousePressEvent( QMouseEvent * me ); + virtual void mouseReleaseEvent( QMouseEvent * me ); + virtual void mouseMoveEvent( QMouseEvent * me ); + virtual void keyPressEvent( QKeyEvent * ke ); + virtual void keyReleaseEvent( QKeyEvent * ke ); + virtual void focusOutEvent( QFocusEvent * _fe ); private: @@ -86,6 +85,8 @@ private: static QPixmap * s_whiteKeyPressedPm; static QPixmap * s_blackKeyPressedPm; + bool m_pressedKeys[NOTES_PER_OCTAVE * OCTAVES]; + QScrollBar * m_pianoScroll; channelTrack * m_channelTrack; tones m_startTone; // first key when drawing diff --git a/include/setup_dialog.h b/include/setup_dialog.h index ff9e03b01..4a428029e 100644 --- a/include/setup_dialog.h +++ b/include/setup_dialog.h @@ -40,7 +40,7 @@ #endif #include "audio_device.h" -#include "midi_device.h" +#include "midi_client.h" class QComboBox; @@ -98,7 +98,7 @@ private: typedef QMap aswMap; - typedef QMap mswMap; + typedef QMap mswMap; QComboBox * m_audioInterfaces; aswMap m_audioIfaceSetupWidgets; diff --git a/include/song_editor.h b/include/song_editor.h index 3bfc8e00c..a84dc4b67 100644 --- a/include/song_editor.h +++ b/include/song_editor.h @@ -63,7 +63,7 @@ class visualizationWidget; const int MIN_BPM = 10; const int DEFAULT_BPM = 140; -const int MAX_BPM = 1000; +const int MAX_BPM = 999; const Uint16 MAX_SONG_LENGTH = 9999; diff --git a/include/track_container.h b/include/track_container.h index 9ddd6fcce..93da8f984 100644 --- a/include/track_container.h +++ b/include/track_container.h @@ -119,6 +119,9 @@ private: protected: virtual void wheelEvent( QWheelEvent * _we ); + private: + trackContainer * m_trackContainer; + } ; diff --git a/plugins/Makefile.am b/plugins/Makefile.am index cd149a68c..0a698a26c 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -1,2 +1,7 @@ -SUBDIRS = audio_file_processor ladspa_sine_1063 midi_out plucked_string_synth triple_oscillator vestige +if HAVE_LIBFST +VESTIGE_SUBDIR=vestige +endif + +SUBDIRS = audio_file_processor ladspa_sine_1063 midi_out plucked_string_synth triple_oscillator $(VESTIGE_SUBDIR) + diff --git a/plugins/audio_file_processor/Makefile.am b/plugins/audio_file_processor/Makefile.am index 3c8af8bc5..f20564e2c 100644 --- a/plugins/audio_file_processor/Makefile.am +++ b/plugins/audio_file_processor/Makefile.am @@ -14,7 +14,6 @@ AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="audiofileprocessor" MOC_FILES = ./audio_file_processor.moc BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h - EMBEDDED_RESOURCES = $(wildcard *png) ./embedded_resources.h: $(EMBEDDED_RESOURCES) diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 8cc9de7fc..9dadb2280 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -68,7 +68,7 @@ plugin::descriptor audiofileprocessor_plugin_descriptor = { STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), "AudioFileProcessor", - QT_TRANSLATE_NOOP( "plugin", + QT_TRANSLATE_NOOP( "pluginBrowser", "simple sampler with various settings for " "using samples (e.g. drums) in a channel" ), "Tobias Doerffel ", @@ -185,9 +185,9 @@ audioFileProcessor::audioFileProcessor( channelTrack * _channel_track ) : "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->setRange( 0.0f, 1.0f, 0.00001f ); m_startKnob->move( 46, 114 ); - m_startKnob->setValue( 0.0, TRUE ); + m_startKnob->setValue( 0.0f, TRUE ); m_startKnob->setHintText( tr( "Startpoint:" )+" ", "" ); m_startKnob->setLabel( tr( "START" ) ); connect( m_startKnob, SIGNAL( valueChanged( float ) ), this, @@ -204,9 +204,9 @@ audioFileProcessor::audioFileProcessor( channelTrack * _channel_track ) : "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->setRange( 0.0f, 1.0f, 0.00001f ); m_endKnob->move( 84, 114 ); - m_endKnob->setValue( 1.0, TRUE ); + m_endKnob->setValue( 1.0f, TRUE ); m_endKnob->setHintText( tr( "Endpoint:" )+" ", "" ); m_endKnob->setLabel( tr( "END" ) ); connect( m_endKnob, SIGNAL( valueChanged( float ) ), this, diff --git a/plugins/plucked_string_synth/plucked_string_synth.cpp b/plugins/plucked_string_synth/plucked_string_synth.cpp index 1ebfbc701..6880f83c7 100644 --- a/plugins/plucked_string_synth/plucked_string_synth.cpp +++ b/plugins/plucked_string_synth/plucked_string_synth.cpp @@ -54,7 +54,7 @@ plugin::descriptor pluckedstringsynth_plugin_descriptor = { STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), "PluckedStringSynth", - QT_TRANSLATE_NOOP( "plugin", + QT_TRANSLATE_NOOP( "pluginBrowser", "cheap synthesis of guitar/harp-like sounds" ), "Tobias Doerffel ", 0x0100, diff --git a/plugins/triple_oscillator/triple_oscillator.cpp b/plugins/triple_oscillator/triple_oscillator.cpp index 4f13bb115..2885f00eb 100644 --- a/plugins/triple_oscillator/triple_oscillator.cpp +++ b/plugins/triple_oscillator/triple_oscillator.cpp @@ -63,7 +63,7 @@ plugin::descriptor tripleoscillator_plugin_descriptor = { STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), "TripleOscillator", - QT_TRANSLATE_NOOP( "plugin", + QT_TRANSLATE_NOOP( "pluginBrowser", "three powerful oscillators you can modulate " "in several ways" ), "Tobias Doerffel ", diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 3f7caf28e..1b8101a91 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -67,7 +67,7 @@ plugin::descriptor vestige_plugin_descriptor = { STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), "VeSTige", - QT_TRANSLATE_NOOP( "plugin", + QT_TRANSLATE_NOOP( "pluginBrowser", "experimental VST-hoster for using VST-plugins " "within LMMS" ), "Tobias Doerffel ", diff --git a/src/core/arp_and_chords_tab_widget.cpp b/src/core/arp_and_chords_tab_widget.cpp index 875f4c85d..1483d9b77 100644 --- a/src/core/arp_and_chords_tab_widget.cpp +++ b/src/core/arp_and_chords_tab_widget.cpp @@ -44,6 +44,11 @@ #endif +#ifdef HAVE_STDLIB_H +#include +#endif + + #include "arp_and_chords_tab_widget.h" #include "embed.h" #include "note_play_handle.h" @@ -54,10 +59,7 @@ #include "tooltip.h" #include "gui_templates.h" #include "tempo_sync_knob.h" - -#ifdef HAVE_STDLIB_H -#include -#endif +#include "channel_track.h" @@ -194,12 +196,10 @@ const int ARP_GROUPBOX_HEIGHT = 200 - ARP_GROUPBOX_Y; -arpAndChordsTabWidget::arpAndChordsTabWidget( channelTrack * _channel_track, - QWidget * _parent ) : - QWidget( _parent ), +arpAndChordsTabWidget::arpAndChordsTabWidget( channelTrack * _channel_track ) : + QWidget( _channel_track->tabWidgetParent() ), settings(), - m_arpDirection( UP ), - m_channelTrack( _channel_track ) + m_arpDirection( UP ) { m_chordsGroupBox = new groupBox( tr( "CHORDS" ), this ); m_chordsGroupBox->setGeometry( CHORDS_GROUPBOX_X, CHORDS_GROUPBOX_Y, @@ -604,41 +604,41 @@ void arpAndChordsTabWidget::processNote( notePlayHandle * _n ) void arpAndChordsTabWidget::saveSettings( QDomDocument & _doc, QDomElement & _parent ) { - QDomElement elw_de = _doc.createElement( nodeName() ); - elw_de.setAttribute( "chorddisabled", QString::number( + QDomElement act_de = _doc.createElement( nodeName() ); + act_de.setAttribute( "chorddisabled", QString::number( !m_chordsGroupBox->isActive() ) ); #ifdef QT4 - elw_de.setAttribute( "chord", QString::number( + act_de.setAttribute( "chord", QString::number( m_chordsComboBox->currentIndex() ) ); #else - elw_de.setAttribute( "chord", QString::number( + act_de.setAttribute( "chord", QString::number( m_chordsComboBox->currentItem() ) ); #endif - elw_de.setAttribute( "chordrange", QString::number( + act_de.setAttribute( "chordrange", QString::number( m_chordRangeKnob->value() ) ); - elw_de.setAttribute( "arpdisabled", QString::number( + act_de.setAttribute( "arpdisabled", QString::number( !m_arpGroupBox->isActive() ) ); #ifdef QT4 - elw_de.setAttribute( "arp", QString::number( + act_de.setAttribute( "arp", QString::number( m_arpComboBox->currentIndex() ) ); #else - elw_de.setAttribute( "arp", QString::number( + act_de.setAttribute( "arp", QString::number( m_arpComboBox->currentItem() ) ); #endif - elw_de.setAttribute( "arprange", QString::number( + act_de.setAttribute( "arprange", QString::number( m_arpRangeKnob->value() ) ); - elw_de.setAttribute( "arptime", QString::number( + act_de.setAttribute( "arptime", QString::number( m_arpTimeKnob->value() ) ); - elw_de.setAttribute( "arpgate", QString::number( + act_de.setAttribute( "arpgate", QString::number( m_arpGateKnob->value() ) ); - elw_de.setAttribute( "arpdir", QString::number( + act_de.setAttribute( "arpdir", QString::number( m_arpDirection ) ); - elw_de.setAttribute( "arpsyncmode", QString::number( - ( int ) m_arpTimeKnob->getSyncMode() ) ); + act_de.setAttribute( "arpsyncmode", QString::number( + ( int ) m_arpTimeKnob->getSyncMode() ) ); - _parent.appendChild( elw_de ); + _parent.appendChild( act_de ); } diff --git a/src/core/envelope_tab_widget.cpp b/src/core/envelope_tab_widget.cpp index 87c7f8e0f..543e56908 100644 --- a/src/core/envelope_tab_widget.cpp +++ b/src/core/envelope_tab_widget.cpp @@ -52,6 +52,8 @@ #include "tab_widget.h" #include "embed.h" #include "gui_templates.h" +#include "channel_track.h" + const int TARGETS_TABWIDGET_X = 4; @@ -82,11 +84,9 @@ static const QString targetNames[envelopeTabWidget::TARGET_COUNT][2] = -envelopeTabWidget::envelopeTabWidget( channelTrack * _channel_track, - QWidget * _parent ) : - QWidget( _parent ), - settings(), - m_channelTrack( _channel_track ) +envelopeTabWidget::envelopeTabWidget( channelTrack * _channel_track ) : + QWidget( _channel_track->tabWidgetParent() ), + settings() { m_targetsTabWidget = new tabWidget( tr( "TARGET" ), this ); diff --git a/src/core/file_browser.cpp b/src/core/file_browser.cpp new file mode 100644 index 000000000..72434e4c1 --- /dev/null +++ b/src/core/file_browser.cpp @@ -0,0 +1,756 @@ +/* + * file_browser.cpp - implementation of the project-, preset- and sample-file-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 + +#else + +#include +#include + +#endif + + +#include "file_browser.h" +#include "song_editor.h" +#include "bb_editor.h" +#include "embed.h" +#include "channel_track.h" +#include "mmp.h" +#include "preset_preview_play_handle.h" +#include "sample_play_handle.h" +#include "debug.h" +#include "gui_templates.h" +#include "instrument.h" + + + +fileBrowser::fileBrowser( const QString & _path, const QString & _filter, + const QString & _title, const QPixmap & _pm, + QWidget * _parent ) : + sideBarWidget( _title, _pm, _parent ), + m_contextMenuItem( NULL ), + m_path( _path ), + m_filter( _filter ), + m_previewPlayHandle( NULL ) +{ + setWindowTitle( tr( "Browser" ) ); + m_l = new Q3ListView( contentParent() ); + addContentWidget( m_l ); + +#ifdef QT4 + connect( m_l, SIGNAL( mouseButtonPressed( int, Q3ListViewItem *, + const QPoint &, int ) ), + this, SLOT( itemPressed( int, Q3ListViewItem *, + const QPoint &, int ) ) ); + connect( m_l, SIGNAL( mouseButtonClicked( int, Q3ListViewItem *, + const QPoint &, int ) ), + this, SLOT( itemReleased( int, Q3ListViewItem *, + const QPoint &, int ) ) ); + connect( m_l, SIGNAL( doubleClicked( Q3ListViewItem *, + const QPoint &, int ) ), + this, SLOT( itemDoubleClicked( Q3ListViewItem *, + const QPoint &, int ) ) ); + + connect( m_l, SIGNAL( contextMenuRequested( Q3ListViewItem *, + const QPoint &, int ) ), + this, SLOT( contextMenuRequest( Q3ListViewItem *, + const QPoint &, int ) ) ); +#else + connect( m_l, SIGNAL( mouseButtonPressed( int, QListViewItem *, + const QPoint &, int ) ), + this, SLOT( itemPressed( int, QListViewItem *, + const QPoint &, int ) ) ); + connect( m_l, SIGNAL( mouseButtonClicked( int, QListViewItem *, + const QPoint &, int ) ), + this, SLOT( itemReleased( int, QListViewItem *, + const QPoint &, int ) ) ); +/* connect( m_l, SIGNAL( pressed( QListViewItem * ) ), + this, SLOT( itemClicked( QListViewItem * ) ) );*/ + connect( m_l, SIGNAL( doubleClicked( QListViewItem *, + const QPoint &, int ) ), + this, SLOT( itemDoubleClicked( QListViewItem *, + const QPoint &, int ) ) ); + + connect( m_l, SIGNAL( contextMenuRequested( QListViewItem *, + const QPoint &, int ) ), + this, SLOT( contextMenuRequest( QListViewItem *, + const QPoint &, int ) ) ); +#endif + connect( m_l, SIGNAL( selectionChanged() ), this, + SLOT( selectionChanged() ) ); + + m_l->addColumn( tr( "Files" ) ); + m_l->setTreeStepSize( 12 ); + m_l->setDefaultRenameAction( Q3ListView::Accept ); + m_l->setSorting( -1 ); + //setColumnWidthMode (0, Manual); + //setColumnWidth (0, 196); + m_l->setShowToolTips( TRUE ); + //m_l->setGeometry (0, 0, 200, 600); + + m_l->setFont( pointSize<8>( m_l->font() ) ); + + + QPushButton * reload_btn = new QPushButton( embed::getIconPixmap( + "reload" ), tr( "Reload (F5)" ), contentParent() ); + addContentWidget( reload_btn ); + connect( reload_btn, SIGNAL( clicked() ), this, SLOT( reloadTree() ) ); + + reloadTree(); + show(); +} + + + + +fileBrowser::~fileBrowser() +{ +} + + + + +void fileBrowser::reloadTree( void ) +{ + m_l->clear(); + QDir cdir( m_path ); + QStringList files = cdir.entryList( QDir::NoFilter, QDir::Name ); + + // TODO: after dropping qt3-support we can use QStringList's iterator + // which makes it possible to travel through the list in reverse + // direction + + for( csize i = 0; i < files.size(); ++i ) + { + QString cur_file = files[files.size() - i - 1]; + if( cur_file[0] != '.' && + !QFileInfo( m_path + "/" + cur_file ).isDir() +#ifdef QT4 +// TBD +#else + && QDir::match( m_filter, cur_file.lower() ) +#endif + ) + { + (void) new fileItem( m_l, cur_file, m_path ); + } + } + + for( csize i = 0; i < files.size(); ++i ) + { + QString cur_file = files[files.size() - i - 1]; + if( cur_file[0] != '.' && + QFileInfo( m_path + "/" + cur_file ).isDir() ) + { + (void) new directory( m_l, cur_file, m_path, m_filter ); + } + } +} + + + + +void fileBrowser::keyPressEvent( QKeyEvent * _ke ) +{ + if( _ke->key() == Qt::Key_F5 ) + { + reloadTree(); + } + else + { + _ke->ignore(); + } +} + + + + +#ifdef QT4 +void fileBrowser::itemPressed( int _btn, Q3ListViewItem * i, const QPoint &, int ) +#else +void fileBrowser::itemPressed( int _btn, QListViewItem * i, const QPoint &, int ) +#endif +{ + fileItem * f = dynamic_cast( i ); + if( f != NULL && _btn == Qt::LeftButton ) + { + if( m_previewPlayHandle != NULL ) + { + mixer::inst()->removePlayHandle( m_previewPlayHandle ); + m_previewPlayHandle = NULL; + } + if( f->type() == fileItem::SAMPLE_FILE ) + { + samplePlayHandle * s = new samplePlayHandle( + f->fullName() ); + s->setDoneMayReturnTrue( FALSE ); + m_previewPlayHandle = s; + } + else if( f->type() == fileItem::PRESET_FILE ) + { + m_previewPlayHandle = new presetPreviewPlayHandle( + f->fullName() ); + } + if( m_previewPlayHandle != NULL ) + { + mixer::inst()->addPlayHandle( m_previewPlayHandle ); + } + } +} + + + + +#ifdef QT4 +void fileBrowser::itemReleased( int, Q3ListViewItem * i, const QPoint &, int ) +#else +void fileBrowser::itemReleased( int, QListViewItem * i, const QPoint &, int ) +#endif +{ + selectionChanged(); +} + + + + +void fileBrowser::selectionChanged( void ) +{ + if( m_previewPlayHandle != NULL ) + { + // if there're samples shorter than 3 seconds, we don't + // stop them if the user releases mouse-button... + samplePlayHandle * s = dynamic_cast( + m_previewPlayHandle ); + if( s != NULL ) + { + if( s->totalFrames() - s->framesDone() <= + static_cast( + mixer::inst()->sampleRate() * 3 ) ) + { + s->setDoneMayReturnTrue( TRUE ); + m_previewPlayHandle = NULL; + return; + } + } + mixer::inst()->removePlayHandle( m_previewPlayHandle ); + m_previewPlayHandle = NULL; + } +} + + + + +#ifdef QT4 +void fileBrowser::itemDoubleClicked( Q3ListViewItem * i, const QPoint &, int ) +#else +void fileBrowser::itemDoubleClicked( QListViewItem * i, const QPoint &, int ) +#endif +{ + fileItem * f = dynamic_cast( i ); + if( f != NULL ) + { + if( f->type() == fileItem::SAMPLE_FILE ) + { + // samples are per default opened in bb-editor because + // they're likely drum-samples etc. + channelTrack * ct = dynamic_cast( + track::create( + track::CHANNEL_TRACK, + bbEditor::inst() ) ); +#ifdef LMMS_DEBUG + assert( ct != NULL ); +#endif + instrument * afp = ct->loadInstrument( + "audiofileprocessor" ); + if( afp != NULL ) + { + afp->setParameter( "audiofile", f->fullName() ); + } + ct->toggledChannelButton( TRUE ); + } + else if( f->type() == fileItem::PRESET_FILE ) + { + // presets are per default opened in bb-editor + multimediaProject mmp( f->fullName() ); + track * t = track::create( track::CHANNEL_TRACK, + bbEditor::inst() ); + channelTrack * ct = dynamic_cast( t ); + if( ct != NULL ) + { + ct->loadTrackSpecificSettings( mmp.content(). + firstChild(). + toElement() ); + ct->toggledChannelButton( TRUE ); + } + } + else if( f->type() == fileItem::SONG_FILE ) + { + if( songEditor::inst()->mayChangeProject() == TRUE ) + { + songEditor::inst()->loadProject( + f->fullName() ); + } + } + } +} + + + +#ifdef QT4 +void fileBrowser::contextMenuRequest( Q3ListViewItem * i, const QPoint &, int ) +#else +void fileBrowser::contextMenuRequest( QListViewItem * i, const QPoint &, int ) +#endif +{ + fileItem * f = dynamic_cast( i ); + if( f != NULL && ( f->type() == fileItem::SAMPLE_FILE || + f->type() == fileItem::PRESET_FILE ) ) + { + m_contextMenuItem = f; + QMenu * contextMenu = new QMenu( this ); + contextMenu->addAction( tr( "Send to active channel" ), this, + SLOT( sendToActiveChannel() ) ); + contextMenu->addAction( tr( "Open in new channel/Song-Editor" ), + this, + SLOT( openInNewChannelSE() ) ); + contextMenu->addAction( tr( "Open in new channel/B+B Editor" ), + this, + SLOT( openInNewChannelBBE() ) ); + //contextMenu->addSeparator (); + //contextMenu->addAction (tr("Rename"), this, SLOT(renameItem())); + contextMenu->exec( QCursor::pos() ); + m_contextMenuItem = NULL; + delete contextMenu; + } + +} + + + + +void fileBrowser::sendToActiveChannel( void ) +{ + // get all windows opened in the workspace + QWidgetList pl = lmmsMainWin::inst()->workspace()->windowList( +#if QT_VERSION >= 0x030200 + QWorkspace::StackingOrder +#endif + ); +#ifdef QT4 + QListIterator w( pl ); + w.toBack(); + // now we travel through the window-list until we find a channel-track + while( w.hasPrevious() ) + { + channelTrack * ct = dynamic_cast( + w.previous() ); +#else + QWidget * w = pl.last(); + // now we travel through the window-list until we find a channel-track + while( w != NULL ) + { + channelTrack * ct = dynamic_cast( w ); +#endif + if( ct != NULL && ct->isHidden() == FALSE ) + { + // ok, it's a channel-track, so we can apply the + // sample or the preset + if( m_contextMenuItem->type() == fileItem::SAMPLE_FILE ) + { + instrument * afp = ct->loadInstrument( + "audiofileprocessor" ); + if( afp != NULL ) + { + afp->setParameter( "audiofile", + m_contextMenuItem->fullName() ); + } + } + else if( m_contextMenuItem->type() == + fileItem::PRESET_FILE ) + { + multimediaProject mmp( + m_contextMenuItem->fullName() ); + ct->loadTrackSpecificSettings( + mmp.content(). + firstChild(). + toElement() ); + } + ct->toggledChannelButton( TRUE ); + break; + } +#ifndef QT4 + w = pl.prev(); +#endif + } +} + + + + +void fileBrowser::openInNewChannel( trackContainer * _tc ) +{ + if( m_contextMenuItem->type() == fileItem::SAMPLE_FILE ) + { + channelTrack * ct = dynamic_cast( + track::create( track::CHANNEL_TRACK, _tc ) ); +#ifdef LMMS_DEBUG + assert( ct != NULL ); +#endif + instrument * afp = ct->loadInstrument( "audiofileprocessor" ); + if( afp != NULL ) + { + afp->setParameter( "audiofile", + m_contextMenuItem->fullName() ); + } + ct->toggledChannelButton( TRUE ); + } + else if( m_contextMenuItem->type() == fileItem::PRESET_FILE ) + { + multimediaProject mmp( m_contextMenuItem->fullName() ); + track * t = track::create( track::CHANNEL_TRACK, _tc ); + channelTrack * ct = dynamic_cast( t ); + if( ct != NULL ) + { + ct->loadTrackSpecificSettings( mmp.content(). + firstChild(). + toElement() ); + ct->toggledChannelButton( TRUE ); + } + } +} + + + + +void fileBrowser::openInNewChannelSE( void ) +{ + openInNewChannel( songEditor::inst() ); +} + + + + +void fileBrowser::openInNewChannelBBE( void ) +{ + openInNewChannel( bbEditor::inst() ); +} + + + + +void fileBrowser::renameItem( void ) +{ + m_contextMenuItem->startRename( 0 ); +} + + + + + + + +QPixmap * directory::s_folderPixmap = NULL; +QPixmap * directory::s_folderOpenedPixmap = NULL; +QPixmap * directory::s_folderLockedPixmap = NULL; + + +directory::directory( directory * _parent, const QString & _name, + const QString & _path, const QString & _filter ) : + Q3ListViewItem( _parent, _name ), + m_p( _parent ), + m_pix( NULL ), + m_path( _path ), + m_filter( _filter ) +{ + initPixmapStuff(); +} + + + + +directory::directory( Q3ListView * _parent, const QString & _name, + const QString & _path, const QString & _filter ) : + Q3ListViewItem( _parent, _name ), + m_p( NULL ), + m_pix( NULL ), + m_path( _path ), + m_filter( _filter ) +{ + initPixmapStuff(); +} + + + + + +void directory::initPixmapStuff( void ) +{ + if( s_folderPixmap == NULL ) + { + s_folderPixmap = new QPixmap( + embed::getIconPixmap( "folder" ) ); + } + if( s_folderOpenedPixmap == NULL ) + { + s_folderOpenedPixmap = new QPixmap( + embed::getIconPixmap( "folder_opened" ) ); + } + if( s_folderLockedPixmap == NULL ) + { + s_folderLockedPixmap = new QPixmap( + embed::getIconPixmap( "folder_locked" ) ); + } + + if( !QDir( fullName() ).isReadable() ) + { + setPixmap( s_folderLockedPixmap ); + } + else + { + setPixmap( s_folderPixmap ); + } +} + + + + +void directory::setPixmap( QPixmap * _px ) +{ + m_pix = _px; + setup(); + widthChanged( 0 ); + invalidateHeight(); + repaint(); +} + + + + +void directory::setOpen( bool _o ) +{ + if( _o ) + { + setPixmap( s_folderOpenedPixmap ); + } + else + { + setPixmap( s_folderPixmap ); + } + + if( _o && !childCount() ) + { + QString s( fullName() ); + QDir thisDir( s ); + if( !thisDir.isReadable() ) + { + //readable = FALSE; + setExpandable( FALSE ); + return; + } + + listView()->setUpdatesEnabled( FALSE ); + + QStringList files = thisDir.entryList( QDir::NoFilter, + QDir::Name ); + for( csize i = 0; i < files.size(); ++i ) + { + QString cur_file = files[files.size()-i-1]; +#ifdef QT4 + if( cur_file[0] != '.' && !QFileInfo( + thisDir.absolutePath() + "/" + + cur_file ).isDir() && + thisDir.match( m_filter, cur_file.lower() ) + /*QDir::match( FILE_FILTER, cur_file )*/ ) +#else + if( cur_file[0] != '.' && !QFileInfo( + thisDir.absPath() + "/" + + cur_file ).isDir() && + thisDir.match( m_filter, cur_file.lower() ) + /*QDir::match( FILE_FILTER, cur_file )*/ ) +#endif + { + (void) new fileItem( this, cur_file, s ); + } + } + for( csize i = 0; i < files.size(); ++i ) + { + QString cur_file = files[files.size()-i-1]; +#ifdef QT4 + if( cur_file[0] != '.' && QFileInfo( + thisDir.absolutePath() + "/" + + cur_file ).isDir() ) +#else + if( cur_file[0] != '.' && QFileInfo( + thisDir.absPath() + "/" + + cur_file ).isDir() ) +#endif + { + (void) new directory( this, cur_file, s, + m_filter ); + } + } + listView()->setUpdatesEnabled( TRUE ); + } + Q3ListViewItem::setOpen( _o ); +} + + + + +void directory::setup( void ) +{ + setExpandable( TRUE ); + Q3ListViewItem::setup(); +} + + + + + + + +QPixmap * fileItem::s_songFilePixmap = NULL; +QPixmap * fileItem::s_presetFilePixmap = NULL; +QPixmap * fileItem::s_sampleFilePixmap = NULL; +QPixmap * fileItem::s_unknownFilePixmap = NULL; + + +fileItem::fileItem( Q3ListView * _parent, const QString & _name, + const QString & _path ) : + Q3ListViewItem( _parent, _name ), + m_path( _path ) +{ + determineFileType(); + initPixmapStuff(); + setDragEnabled( TRUE ); +} + + + + +fileItem::fileItem( Q3ListViewItem * _parent, const QString & _name, + const QString & _path ) : + Q3ListViewItem( _parent, _name ), + m_path( _path ) +{ + determineFileType(); + initPixmapStuff(); + setDragEnabled( TRUE ); +} + + + + +void fileItem::initPixmapStuff( void ) +{ + if( s_songFilePixmap == NULL ) + { + s_songFilePixmap = new QPixmap( embed::getIconPixmap( + "project_file", 16, 16 ) ); + } + if( s_presetFilePixmap == NULL ) + { + s_presetFilePixmap = new QPixmap( embed::getIconPixmap( + "preset_file", 16, 16 ) ); + } + if( s_sampleFilePixmap == NULL ) + { + s_sampleFilePixmap = new QPixmap( embed::getIconPixmap( + "sound_file", 16, 16 ) ); + } + if( s_unknownFilePixmap == NULL ) + { + s_unknownFilePixmap = new QPixmap( embed::getIconPixmap( + "unknown_file" ) ); + } + + switch( m_type ) + { + case SONG_FILE: m_pix = s_songFilePixmap; break; + case PRESET_FILE: m_pix = s_presetFilePixmap; break; + case SAMPLE_FILE: m_pix = s_sampleFilePixmap; break; + case UNKNOWN: m_pix = s_unknownFilePixmap; break; + } +} + + + + +void fileItem::determineFileType( void ) +{ +#ifdef QT4 + QString ext = QFileInfo( fullName() ).suffix().toLower(); +#else + QString ext = QFileInfo( fullName() ).extension( FALSE ).toLower(); +#endif + if( ext == "mmp" ) + { + m_type = SONG_FILE; + } + else if( ext == "xml" ) + { + multimediaProject::projectTypes t = + multimediaProject::typeOfFile( fullName() ); + if( t == multimediaProject::SONG_PROJECT ) + { + m_type = SONG_FILE; + } + else if( t == multimediaProject::CHANNEL_SETTINGS ) + { + m_type = PRESET_FILE; + } + else + { + m_type = UNKNOWN; + } + } + else if( ext == "csf" ) + { + m_type = PRESET_FILE; + } + else if( ext == "wav" || ext == "ogg" || ext == "mp3" || + ext == "aiff" || ext == "aif" || ext == "voc" || + ext == "au" || ext == "raw" ) + { + m_type = SAMPLE_FILE; + } + else + { + m_type = UNKNOWN; + } +} + + + + + +#include "file_browser.moc" + diff --git a/src/core/instrument.cpp b/src/core/instrument.cpp index 302f3a0a9..3b6e0f332 100644 --- a/src/core/instrument.cpp +++ b/src/core/instrument.cpp @@ -28,7 +28,7 @@ instrument::instrument( channelTrack * _channel_track, const QString & _name ) : - QWidget( _channel_track->pluginParent() ), + QWidget( _channel_track->tabWidgetParent() ), plugin( _name, INSTRUMENT ), m_channelTrack( _channel_track ), m_valid( TRUE ) diff --git a/src/core/midi_tab_widget.cpp b/src/core/midi_tab_widget.cpp new file mode 100644 index 000000000..51dfb4065 --- /dev/null +++ b/src/core/midi_tab_widget.cpp @@ -0,0 +1,196 @@ +/* + * midi_tab_widget.cpp - tab-widget in channel-track-window for setting up + * MIDI-related stuff + * + * 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 + +#else + +#include + +#endif + + +#include "midi_tab_widget.h" +#include "channel_track.h" +#include "midi_port.h" +#include "tab_widget.h" +#include "led_checkbox.h" +#include "lcd_spinbox.h" +#include "tooltip.h" +#include "song_editor.h" + + + + +midiTabWidget::midiTabWidget( channelTrack * _channel_track, + midiPort * _port ) : + QWidget( _channel_track->tabWidgetParent() ), + settings(), + m_channelTrack( _channel_track ), + m_midiPort( _port ) +{ + m_setupTabWidget = new tabWidget( tr( "MIDI-SETUP FOR THIS CHANNEL" ), + this ); + m_setupTabWidget->setGeometry( 4, 5, 238, 130 ); + + + m_inputChannelSpinBox = new lcdSpinBox( 0, MIDI_CHANNEL_COUNT, 3, + m_setupTabWidget ); + m_inputChannelSpinBox->addTextForValue( 0, "---" ); + m_inputChannelSpinBox->setValue( m_midiPort->inputChannel() + 1 ); + m_inputChannelSpinBox->setLabel( tr( "CHANNEL" ) ); + m_inputChannelSpinBox->move( 190, 30 ); + connect( m_inputChannelSpinBox, SIGNAL( valueChanged( int ) ), + this, SLOT( inputChannelChanged( int ) ) ); + + m_outputChannelSpinBox = new lcdSpinBox( 0, MIDI_CHANNEL_COUNT, 3, + m_setupTabWidget ); + m_outputChannelSpinBox->addTextForValue( 0, "---" ); + m_outputChannelSpinBox->setValue( m_midiPort->outputChannel() + 1 ); + m_outputChannelSpinBox->setLabel( tr( "CHANNEL" ) ); + m_outputChannelSpinBox->move( 190, 60 ); + connect( m_outputChannelSpinBox, SIGNAL( valueChanged( int ) ), + this, SLOT( outputChannelChanged( int ) ) ); + + + m_receiveCheckBox = new ledCheckBox( tr( "RECEIVE MIDI-EVENTS" ), + m_setupTabWidget ); + m_receiveCheckBox->move( 10, 34 ); + connect( m_receiveCheckBox, SIGNAL( toggled( bool ) ), + this, SLOT( midiPortModeToggled( bool ) ) ); + connect( m_receiveCheckBox, SIGNAL( toggled( bool ) ), + m_inputChannelSpinBox, SLOT( setEnabled( bool ) ) ); + + + m_sendCheckBox = new ledCheckBox( tr( "SEND MIDI-EVENTS" ), + m_setupTabWidget ); + m_sendCheckBox->move( 10, 64 ); + connect( m_sendCheckBox, SIGNAL( toggled( bool ) ), + this, SLOT( midiPortModeToggled( bool ) ) ); + connect( m_sendCheckBox, SIGNAL( toggled( bool ) ), + m_outputChannelSpinBox, SLOT( setEnabled( bool ) ) ); + + + m_routeCheckBox = new ledCheckBox( tr( "SEND RECEIVED MIDI-EVENTS" ), + m_setupTabWidget ); + m_routeCheckBox->setChecked( + m_channelTrack->midiEventRoutingEnabled() ); + m_routeCheckBox->move( 10, 100 ); + connect( m_sendCheckBox, SIGNAL( toggled( bool ) ), + m_channelTrack, SLOT( toggleMidiEventRouting( bool ) ) ); + + midiPort::modes m = m_midiPort->mode(); + m_receiveCheckBox->setChecked( m == midiPort::INPUT || + m == midiPort::DUPLEX ); + m_sendCheckBox->setChecked( m == midiPort::OUTPUT || + m == midiPort::DUPLEX ); + +} + + + + +midiTabWidget::~midiTabWidget() +{ +} + + + + +void midiTabWidget::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + QDomElement mw_de = _doc.createElement( nodeName() ); + mw_de.setAttribute( "inputchannel", QString::number( + m_inputChannelSpinBox->value() ) ); + mw_de.setAttribute( "outputchannel", QString::number( + m_outputChannelSpinBox->value() ) ); + mw_de.setAttribute( "receive", QString::number( + m_receiveCheckBox->isChecked() ) ); + mw_de.setAttribute( "send", QString::number( + m_sendCheckBox->isChecked() ) ); + mw_de.setAttribute( "route", QString::number( + m_routeCheckBox->isChecked() ) ); + + _parent.appendChild( mw_de ); +} + + + + +void midiTabWidget::loadSettings( const QDomElement & _this ) +{ + m_inputChannelSpinBox->setValue( _this.attribute( "inputchannel" + ).toInt() ); + m_outputChannelSpinBox->setValue( _this.attribute( "outputchannel" + ).toInt() ); + m_receiveCheckBox->setChecked( _this.attribute( "receive" ).toInt() ); + m_sendCheckBox->setChecked( _this.attribute( "send" ).toInt() ); + m_routeCheckBox->setChecked( _this.attribute( "route" ).toInt() ); +} + + + + +void midiTabWidget::inputChannelChanged( int _new_chnl ) +{ + m_midiPort->setInputChannel( _new_chnl - 1 ); + songEditor::inst()->setModified(); +} + + + + +void midiTabWidget::outputChannelChanged( int _new_chnl ) +{ + m_midiPort->setOutputChannel( _new_chnl - 1 ); + songEditor::inst()->setModified(); +} + + + + +void midiTabWidget::midiPortModeToggled( bool ) +{ + // this small lookup-table makes everything easier + static const midiPort::modes modeTable[2][2] = + { + { midiPort::DUMMY, midiPort::OUTPUT }, + { midiPort::INPUT, midiPort::DUPLEX } + } ; + m_midiPort->setMode( modeTable[m_receiveCheckBox->isChecked()] + [m_sendCheckBox->isChecked()] ); + + songEditor::inst()->setModified(); +} + + + + +#include "midi_tab_widget.moc" + diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp index 77f6c905e..9d71bde1d 100644 --- a/src/core/mixer.cpp +++ b/src/core/mixer.cpp @@ -32,7 +32,7 @@ #include "config_mgr.h" #include "audio_device.h" -#include "midi_device.h" +#include "midi_client.h" // platform-specific audio-interface-classes #include "audio_alsa.h" @@ -100,7 +100,7 @@ mixer::mixer() : m_audioDev = tryAudioDevices(); - m_midiDev = tryMIDIDevices(); + m_midiClient = tryMIDIClients(); for( int i = 0; i < MAX_SAMPLE_PACKETS; ++i ) @@ -303,7 +303,7 @@ void mixer::run( void ) // all remaining notes etc. would be played until their end void mixer::clear( void ) { - m_midiDev->noteOffAll(); + // TODO: m_midiClient->noteOffAll(); for( playHandleVector::iterator it = m_playHandles.begin(); it != m_playHandles.end(); ++it ) { @@ -639,17 +639,18 @@ audioDevice * mixer::tryAudioDevices( void ) -midiDevice * mixer::tryMIDIDevices( void ) +midiClient * mixer::tryMIDIClients( void ) { - QString dev_name = configManager::inst()->value( "mixer", "mididev" ); + QString client_name = configManager::inst()->value( "mixer", + "midiclient" ); #ifdef ALSA_SUPPORT - if( dev_name == midiALSARaw::name() || dev_name == "" ) + if( client_name == midiALSARaw::name() || client_name == "" ) { midiALSARaw * malsa = new midiALSARaw(); if( malsa->isRunning() ) { - m_midiDevName = midiALSARaw::name(); + m_midiClientName = midiALSARaw::name(); return( malsa ); } delete malsa; @@ -657,22 +658,22 @@ midiDevice * mixer::tryMIDIDevices( void ) #endif #ifdef OSS_SUPPORT - if( dev_name == midiOSS::name() || dev_name == "" ) + if( client_name == midiOSS::name() || client_name == "" ) { midiOSS * moss = new midiOSS(); if( moss->isRunning() ) { - m_midiDevName = midiOSS::name(); + m_midiClientName = midiOSS::name(); return( moss ); } delete moss; } #endif - printf( "Couldn't open a MIDI-device, neither with ALSA nor with " - "OSS. Will use dummy-MIDI-device.\n" ); + printf( "Couldn't create MIDI-client, neither with ALSA nor with " + "OSS. Will use dummy-MIDI-client.\n" ); - m_midiDevName = midiDummy::name(); + m_midiClientName = midiDummy::name(); return( new midiDummy() ); } diff --git a/src/core/piano_roll.cpp b/src/core/piano_roll.cpp index c58684d5e..9c02fa090 100644 --- a/src/core/piano_roll.cpp +++ b/src/core/piano_roll.cpp @@ -46,17 +46,25 @@ #endif +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif + +#include + + #include "piano_roll.h" #include "song_editor.h" #include "pattern.h" #include "embed.h" #include "crystal_button.h" #include "pixmap_button.h" -#include "note_play_handle.h" +#include "templates.h" #include "gui_templates.h" #include "timeline.h" #include "channel_track.h" #include "tooltip.h" +#include "midi.h" extern tones whiteKeys[]; // defined in piano_widget.cpp @@ -518,10 +526,10 @@ void pianoRoll::setCurrentPattern( pattern * _new_pattern ) // remove all connections to other channel-tracks disconnect( this, SLOT( recordNote( const note & ) ) ); - // and now connect to noetFromMidiDeviceDone of channel so that - // we receive note-off-events from midi-keyboard for recording it + // and now connect to noteDone()-signal of channel so that + // we receive note-off-events from it's midi-port for recording it connect( m_pattern->getChannelTrack(), - SIGNAL( noteFromMidiDeviceDone( const note & ) ), + SIGNAL( noteDone( const note & ) ), this, SLOT( recordNote( const note & ) ) ); setWindowTitle( tr( "Piano-Roll - %1" ).arg( m_pattern->name() ) ); @@ -1260,7 +1268,8 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me ) songEditor::inst()->playing() == FALSE ) { m_pattern->getChannelTrack()->processInEvent( - midiEvent( NOTE_ON, 0, key_num, vol ) ); + midiEvent( NOTE_ON, 0, key_num, vol ), + midiTime() ); } } } @@ -1276,12 +1285,13 @@ void pianoRoll::mouseReleaseEvent( QMouseEvent * _me ) { m_pattern->getChannelTrack()->processInEvent( midiEvent( NOTE_OFF, 0, - m_currentNote->key() ) ); + m_currentNote->key() ), midiTime() ); } else { m_pattern->getChannelTrack()->processInEvent( - midiEvent( NOTE_OFF, 0, getKey( _me->y() ) ) ); + midiEvent( NOTE_OFF, 0, getKey( _me->y() ) ), + midiTime() ); } } @@ -1325,7 +1335,8 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me ) edit_note == FALSE ) { m_pattern->getChannelTrack()->processInEvent( - midiEvent( NOTE_OFF, 0, released_key ) ); + midiEvent( NOTE_OFF, 0, released_key ), + midiTime() ); if( #ifdef QT4 _me->buttons() & @@ -1340,7 +1351,8 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me ) { m_pattern->getChannelTrack()->processInEvent( midiEvent( NOTE_ON, 0, key_num, - DEFAULT_VOLUME ) ); + DEFAULT_VOLUME ), + midiTime() ); } } if( _me->x() <= WHITE_KEY_WIDTH ) @@ -1361,12 +1373,10 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me ) MIN_VOLUME, MAX_VOLUME ); m_currentNote->setVolume( vol ); - if( m_pattern->getChannelTrack()->keyPressed( - m_currentNote->key() ) == TRUE ) - { - m_pattern->getChannelTrack()->noteForKey( m_currentNote->key() - )->setVolume( vol ); - } + m_pattern->getChannelTrack()->processInEvent( + midiEvent( KEY_PRESSURE, 0, key_num, + vol ), + midiTime() ); } } else if( m_currentNote != NULL && diff --git a/src/core/piano_widget.cpp b/src/core/piano_widget.cpp index 5d319cf12..7069eb3dd 100644 --- a/src/core/piano_widget.cpp +++ b/src/core/piano_widget.cpp @@ -40,7 +40,8 @@ #include "piano_widget.h" #include "channel_track.h" -#include "note_play_handle.h" +#include "midi.h" +#include "templates.h" #include "embed.h" @@ -106,6 +107,11 @@ pianoWidget::pianoWidget (channelTrack * _parent ) : "black_key_pressed" ) ); } + for( int i = 0; i < NOTES_PER_OCTAVE * OCTAVES; ++i ) + { + m_pressedKeys[i] = FALSE; + } + #ifdef QT4 m_pianoScroll = new QScrollBar( Qt::Horizontal, this ); m_pianoScroll->setRange( 0, WHITE_KEYS_PER_OCTAVE * ( OCTAVES - 3 ) - @@ -231,7 +237,9 @@ void pianoWidget::mousePressEvent( QMouseEvent * _me ) } // set note on m_channelTrack->processInEvent( - midiEvent( NOTE_ON, 0, key_num, vol ) ); + midiEvent( NOTE_ON, 0, key_num, vol ), + midiTime() ); + m_pressedKeys[key_num] = TRUE; } else { @@ -255,7 +263,8 @@ void pianoWidget::mouseReleaseEvent( QMouseEvent * _me ) int released_key = getKeyFromMouse( _me->pos() ); m_channelTrack->processInEvent( - midiEvent ( NOTE_OFF, 0, released_key ) ); + midiEvent( NOTE_OFF, 0, released_key, 0 ), midiTime() ); + m_pressedKeys[released_key] = FALSE; // and let the user see that he released a key... :) update(); @@ -294,7 +303,9 @@ void pianoWidget::mouseMoveEvent( QMouseEvent * _me ) if( key_num != released_key ) { m_channelTrack->processInEvent( - midiEvent( NOTE_OFF, 0, released_key ) ); + midiEvent( NOTE_OFF, 0, released_key, 0 ), + midiTime() ); + m_pressedKeys[released_key] = FALSE; #ifdef QT4 if( _me->buttons() & Qt::LeftButton ) #else @@ -304,7 +315,9 @@ void pianoWidget::mouseMoveEvent( QMouseEvent * _me ) if( _me->pos().y() > PIANO_BASE ) { m_channelTrack->processInEvent( - midiEvent( NOTE_ON, 0, key_num, vol ) ); + midiEvent( NOTE_ON, 0, key_num, vol ), + midiTime() ); + m_pressedKeys[key_num] = TRUE; } else { @@ -317,9 +330,11 @@ void pianoWidget::mouseMoveEvent( QMouseEvent * _me ) // and let the user see that he pressed a key... :) update(); } - else if( m_channelTrack->keyPressed( key_num ) == TRUE ) + else if( m_pressedKeys[key_num] == TRUE ) { - m_channelTrack->noteForKey( key_num )->setVolume( vol ); + m_channelTrack->processInEvent( + midiEvent( KEY_PRESSURE, 0, key_num, vol ), + midiTime() ); } } @@ -376,7 +391,9 @@ void pianoWidget::keyPressEvent( QKeyEvent * _ke ) if( _ke->isAutoRepeat() == FALSE && key_num > -1 ) { m_channelTrack->processInEvent( - midiEvent( NOTE_ON, 0, key_num, DEFAULT_VOLUME ) ); + midiEvent( NOTE_ON, 0, key_num, DEFAULT_VOLUME ), + midiTime() ); + m_pressedKeys[key_num] = TRUE; update(); } else @@ -395,7 +412,9 @@ void pianoWidget::keyReleaseEvent( QKeyEvent * _ke ) if( _ke->isAutoRepeat() == FALSE && key_num > -1 ) { m_channelTrack->processInEvent( - midiEvent( NOTE_OFF, 0, key_num ) ); + midiEvent( NOTE_OFF, 0, key_num, 0 ), + midiTime() ); + m_pressedKeys[key_num] = FALSE; update(); } else @@ -407,25 +426,19 @@ void pianoWidget::keyReleaseEvent( QKeyEvent * _ke ) -void pianoWidget::focusInEvent( QFocusEvent * ) -{ - mixer::inst()->getMIDIDevice()->setChannelTrack( m_channelTrack ); -} - - - - void pianoWidget::focusOutEvent( QFocusEvent * ) { - // if we loose focus, we HAVE to note off all "running" notes because + // if we loose focus, we HAVE to note off all running notes because // we don't receive key-release-events anymore and so the notes would // hang otherwise for( int i = 0; i < NOTES_PER_OCTAVE * OCTAVES; ++i ) { - if( m_channelTrack->keyPressed( i ) ) + if( m_pressedKeys[i] == TRUE ) { m_channelTrack->processInEvent( - midiEvent( NOTE_OFF, 0, i ) ); + midiEvent( NOTE_OFF, 0, i, 0 ), + midiTime() ); + m_pressedKeys[i] = FALSE; } } update(); @@ -531,7 +544,7 @@ void pianoWidget::paintEvent( QPaintEvent * ) // draw pressed or not pressed key, depending on state of // current key - if( m_channelTrack->keyPressed( cur_key ) == TRUE ) + if( m_pressedKeys[cur_key] == TRUE ) { p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPressedPm ); } @@ -562,7 +575,7 @@ void pianoWidget::paintEvent( QPaintEvent * ) if( s_key > 0 && KEY_ORDER[(tones)( --s_key ) % NOTES_PER_OCTAVE] == BLACK_KEY ) { - if( m_channelTrack->keyPressed( s_key ) == TRUE ) + if( m_pressedKeys[s_key] == TRUE ) { p.drawPixmap( 0 - WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm ); @@ -581,7 +594,7 @@ void pianoWidget::paintEvent( QPaintEvent * ) { // draw pressed or not pressed key, depending on // state of current key - if( m_channelTrack->keyPressed( cur_key ) == TRUE ) + if( m_pressedKeys[cur_key] == TRUE ) { p.drawPixmap( x + WHITE_KEY_WIDTH / 2, PIANO_BASE, diff --git a/src/core/plugin_browser.cpp b/src/core/plugin_browser.cpp index 75ebc169e..58b98f01b 100644 --- a/src/core/plugin_browser.cpp +++ b/src/core/plugin_browser.cpp @@ -156,7 +156,7 @@ void pluginDescWidget::paintEvent( QPaintEvent * ) f.setBold( FALSE ); p.setFont( pointSize<7>( f ) ); QStringList words = QStringList::split( ' ', - plugin::tr( m_pluginDescriptor.description ) ); + pluginBrowser::tr( m_pluginDescriptor.description ) ); for( QStringList::iterator it = words.begin(); it != words.end(); ++it ) { if( ( *it ).contains( '-' ) ) diff --git a/src/core/setup_dialog.cpp b/src/core/setup_dialog.cpp index 4112887e0..f39a33686 100644 --- a/src/core/setup_dialog.cpp +++ b/src/core/setup_dialog.cpp @@ -328,11 +328,11 @@ setupDialog::setupDialog( configTabs _tab_to_open ) : #ifdef QT4 m_midiInterfaces->setCurrentIndex( m_midiInterfaces->findText( - mixer::inst()->midiDevName() ) ); + mixer::inst()->midiClientName() ) ); #else - m_midiInterfaces->setCurrentText( mixer::inst()->midiDevName() ); + m_midiInterfaces->setCurrentText( mixer::inst()->midiClientName() ); #endif - m_midiIfaceSetupWidgets[mixer::inst()->midiDevName()]->show(); + m_midiIfaceSetupWidgets[mixer::inst()->midiClientName()]->show(); connect( m_midiInterfaces, SIGNAL( activated( const QString & ) ), this, SLOT( midiInterfaceChanged( const QString & ) ) ); diff --git a/src/core/song_editor.cpp b/src/core/song_editor.cpp index 4673e4e83..60a2cc444 100644 --- a/src/core/song_editor.cpp +++ b/src/core/song_editor.cpp @@ -75,7 +75,7 @@ #include "bb_track.h" #include "channel_track.h" #include "mmp.h" -#include "midi_device.h" +#include "midi_client.h" #include "timeline.h" #include "pattern.h" #include "piano_roll.h" diff --git a/src/core/track_container.cpp b/src/core/track_container.cpp index 6c191d907..34d1a69c6 100644 --- a/src/core/track_container.cpp +++ b/src/core/track_container.cpp @@ -416,7 +416,8 @@ void trackContainer::updateScrollArea( void ) trackContainer::scrollArea::scrollArea( trackContainer * _parent ) : - QScrollArea( _parent ) + QScrollArea( _parent ), + m_trackContainer( _parent ) { setFrameStyle( QFrame::NoFrame ); setHorizontalScrollBarPolicy( @@ -444,7 +445,7 @@ void trackContainer::scrollArea::wheelEvent( QWheelEvent * _we ) // 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 ); + m_trackContainer->wheelEvent( _we ); if( !_we->isAccepted() ) { QScrollArea::wheelEvent( _we ); diff --git a/src/midi/midi_alsa_raw.cpp b/src/midi/midi_alsa_raw.cpp index 8fc8a1b68..c24ee2982 100644 --- a/src/midi/midi_alsa_raw.cpp +++ b/src/midi/midi_alsa_raw.cpp @@ -1,5 +1,5 @@ /* - * midi_alsa_raw.cpp - midi-device-driver for RawMIDI via ALSA + * midi_alsa_raw.cpp - midi-client for RawMIDI via ALSA * * Linux MultiMedia Studio * Copyright (c) 2004-2005 Tobias Doerffel @@ -46,8 +46,8 @@ #ifdef ALSA_SUPPORT -midiALSARaw::midiALSARaw( channelTrack * _ct ) : - midiDevice( _ct ), +midiALSARaw::midiALSARaw( void ) : + midiRawClient(), QThread(), m_inputp( &m_input ), m_outputp( &m_output ), @@ -184,14 +184,12 @@ void midiALSARaw::run( void ) break; } if( err == 0 ) + { continue; + } for( int i = 0; i < err; ++i ) { - const midiEvent * midi_event = parseData( buf[i] ); - if( midi_event != NULL ) - { - processInEvent( *midi_event ); - } + parseData( buf[i] ); } } @@ -201,7 +199,7 @@ void midiALSARaw::run( void ) midiALSARaw::setupWidget::setupWidget( QWidget * _parent ) : - midiDevice::setupWidget( midiALSARaw::name(), _parent ) + midiRawClient::setupWidget( midiALSARaw::name(), _parent ) { m_device = new QLineEdit( midiALSARaw::probeDevice(), this ); m_device->setGeometry( 10, 20, 160, 20 ); diff --git a/src/midi/midi_client.cpp b/src/midi/midi_client.cpp new file mode 100644 index 000000000..d4b8bde10 --- /dev/null +++ b/src/midi/midi_client.cpp @@ -0,0 +1,339 @@ +/* + * midi_client.cpp - base-class for MIDI-clients like ALSA-sequencer-client + * + * Linux MultiMedia Studio + * Copyright (_c) 2004-2005 Tobias Doerffel + * This file partly contains code from Fluidsynth, Peter Hanappe + * + * 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" +#ifndef QT4 +#include +#endif + +#include "midi_client.h" +/*#include "midi_mapper.h"*/ +#include "templates.h" +#include "midi_port.h" + + +#include "midi_alsa_raw.h" +#include "midi_oss.h" +#include "midi_dummy.h" + + + +midiClient::midiClient( void ) +{ +} + + + + +midiClient::~midiClient() +{ + //TODO: noteOffAll(); +} + + + + +void midiClient::removePort( midiPort * _port ) +{ + vvector::iterator it = qFind( m_midiPorts.begin(), + m_midiPorts.end(), + _port ); + if( it != m_midiPorts.end() ) + { + m_midiPorts.erase( it ); + } +} + + + + + + + + +midiRawClient::midiRawClient() : + midiClient() +{ +} + + + + +midiRawClient::~midiRawClient() +{ +} + + + + +midiPort * FASTCALL midiRawClient::createPort( midiEventProcessor * _mep, + const QString & _desired_name ) +{ + midiPort * mp = new midiPort( this, _mep, _desired_name ); + addPort( mp ); + return( mp ); +} + + + + +// raw-MIDI-clients to not have real ports, therefore the name doesn't matter +// and we can dummy-implement validation-method +void midiRawClient::validatePortName( midiPort * ) +{ +} + + + + +void midiRawClient::parseData( const Uint8 _c ) +{ + /*********************************************************************/ + /* 'Process' system real-time messages */ + /*********************************************************************/ + /* There are not too many real-time messages that are of interest here. + * They can occur anywhere, even in the middle of a noteon message! + * Real-time range: 0xF8 .. 0xFF + * Note: Real-time does not affect (running) status. + */ + if( _c >= 0xF8 ) + { + if( _c == MIDI_SYSTEM_RESET ) + { + m_midiParseData.m_midiEvent.m_type = MIDI_SYSTEM_RESET; + m_midiParseData.m_status = 0; + processParsedEvent(); + } + return; + } + + /*********************************************************************/ + /* 'Process' system common messages (again, just skip them) */ + /*********************************************************************/ + /* There are no system common messages that are of interest here. + * System common range: 0xF0 .. 0xF7 + */ + if( _c > 0xF0 ) + { + /* MIDI spec say: To ignore a non-real-time message, just discard all + * data up to the next status byte. And our parser will ignore data + * that is received without a valid status. + * Note: system common cancels running status. */ + m_midiParseData.m_status = 0; + return; + } + + /*********************************************************************/ + /* Process voice category messages: */ + /*********************************************************************/ + /* Now that we have handled realtime and system common messages, only + * voice messages are left. + * Only a status byte has bit # 7 set. + * So no matter the status of the parser (in case we have lost sync), + * as soon as a byte >= 0x80 comes in, we are dealing with a status byte + * and start a new event. + */ + if( _c & 0x80 ) + { + m_midiParseData.m_channel = _c & 0x0F; + m_midiParseData.m_status = _c & 0xF0; + /* The event consumes x bytes of data... + (subtract 1 for the status byte) */ + m_midiParseData.m_bytesTotal = eventLength( + m_midiParseData.m_status ) - 1; + /* of which we have read 0 at this time. */ + m_midiParseData.m_bytes = 0; + return; + } + + /*********************************************************************/ + /* Process data */ + /*********************************************************************/ + /* If we made it this far, then the received char belongs to the data + * of the last event. */ + if( m_midiParseData.m_status == 0 ) + { + /* We are not interested in the event currently received. + Discard the data. */ + return; + } + + /* Store the first couple of bytes */ + if( m_midiParseData.m_bytes < RAW_MIDI_PARSE_BUF_SIZE ) + { + m_midiParseData.m_buffer[m_midiParseData.m_bytes] = _c; + } + ++m_midiParseData.m_bytes; + + /* Do we still need more data to get this event complete? */ + if( m_midiParseData.m_bytes < m_midiParseData.m_bytesTotal ) + { + return; + } + + /*********************************************************************/ + /* Send the event */ + /*********************************************************************/ + /* The event is ready-to-go. About 'running status': + * + * The MIDI protocol has a built-in compression mechanism. If several + * similar events are sent in-a-row, for example note-ons, then the + * event type is only sent once. For this case, the last event type + * (status) is remembered. + * We simply keep the status as it is, just reset the parameter counter. + * If another status byte comes in, it will overwrite the status. + */ + m_midiParseData.m_midiEvent.m_type = static_cast( + m_midiParseData.m_status ); + m_midiParseData.m_midiEvent.m_channel = m_midiParseData.m_channel; + m_midiParseData.m_bytes = 0; /* Related to running status! */ + switch( m_midiParseData.m_midiEvent.m_type ) + { + case NOTE_OFF: + case NOTE_ON: + case KEY_PRESSURE: + case CONTROL_CHANGE: + case PROGRAM_CHANGE: + case CHANNEL_PRESSURE: + m_midiParseData.m_midiEvent.m_data.m_param[0] = + m_midiParseData.m_buffer[0] - NOTES_PER_OCTAVE; + m_midiParseData.m_midiEvent.m_data.m_param[1] = + m_midiParseData.m_buffer[1]; + break; + + case PITCH_BEND: + // Pitch-bend is transmitted with 14-bit precision. + // Note: '|' does here the same as '+' (no common bits), + // but might be faster + m_midiParseData.m_midiEvent.m_data.m_param[0] = + ( ( m_midiParseData.m_buffer[1] * 128 ) | + m_midiParseData.m_buffer[0] ); + break; + + default: + // Unlikely + return; + } + + processParsedEvent(); +} + + + + +void midiRawClient::processParsedEvent() +{ + for( csize i = 0; i < m_midiPorts.size(); ++i ) + { + m_midiPorts[i]->processInEvent( m_midiParseData.m_midiEvent, + midiTime() ); + } +} + + + + +void midiRawClient::processOutEvent( const midiEvent & _me, + const midiTime & , + const midiPort * _port ) +{ + // TODO: also evaluate _time and queue event if neccessary + switch( _me.m_type ) + { + case NOTE_ON: + case NOTE_OFF: + if( _port->outputChannel() >= 0 ) + { + sendByte( _me.m_type | _port->outputChannel() ); + sendByte( _me.m_data.m_param[0] + + NOTES_PER_OCTAVE ); + sendByte( tLimit( (int) _me.m_data.m_param[1], + 0, 127 ) ); + } + else + { + for( Sint8 i = 0; i < MIDI_CHANNEL_COUNT; ++i ) + { + sendByte( _me.m_type | i ); + sendByte( _me.m_data.m_param[0] + + NOTES_PER_OCTAVE ); + sendByte( tLimit( (int) + _me.m_data.m_param[1], + 0, 127 ) ); + } + } + break; + + default: + break; + } +} + + + + + + +// Taken from Nagano Daisuke's USB-MIDI driver +const Uint8 REMAINS_F0F6[] = +{ + 0, /* 0xF0 */ + 2, /* 0XF1 */ + 3, /* 0XF2 */ + 2, /* 0XF3 */ + 2, /* 0XF4 (Undefined by MIDI Spec, and subject to change) */ + 2, /* 0XF5 (Undefined by MIDI Spec, and subject to change) */ + 1 /* 0XF6 */ +} ; + +const Uint8 REMAINS_80E0[] = +{ + 3, /* 0x8X Note Off */ + 3, /* 0x9X Note On */ + 3, /* 0xAX Poly-key pressure */ + 3, /* 0xBX Control Change */ + 2, /* 0xCX Program Change */ + 2, /* 0xDX Channel pressure */ + 3 /* 0xEX PitchBend Change */ +} ; + + + +// Returns the length of the MIDI message starting with _event. +// Taken from Nagano Daisuke's USB-MIDI driver +Uint8 midiRawClient::eventLength( const Uint8 _event ) +{ + if ( _event < 0xF0 ) + { + return( REMAINS_80E0[( ( _event - 0x80 ) >>4 ) & 0x0F] ); + } + else if ( _event < 0xF7 ) + { + return( REMAINS_F0F6[_event - 0xF0] ); + } + return( 1 ); +} + + diff --git a/src/midi/midi_oss.cpp b/src/midi/midi_oss.cpp index 3245460e4..db1379478 100644 --- a/src/midi/midi_oss.cpp +++ b/src/midi/midi_oss.cpp @@ -1,5 +1,5 @@ /* - * midi_oss.cpp - simple midi-device-driver for OSS + * midi_oss.cpp - OSS-raw-midi-client * * Linux MultiMedia Studio * Copyright (c) 2004-2005 Tobias Doerffel @@ -52,8 +52,8 @@ -midiOSS::midiOSS( channelTrack * _ct ) : - midiDevice( _ct ), +midiOSS::midiOSS( void ) : + midiRawClient(), QThread(), m_midiDev( probeDevice() ), m_quit( FALSE ) @@ -108,7 +108,7 @@ QString midiOSS::probeDevice( void ) -void midiOSS::sendByte( Uint8 _c ) +void midiOSS::sendByte( const Uint8 _c ) { #ifdef QT4 m_midiDev.putChar( _c ); @@ -130,14 +130,10 @@ void midiOSS::run( void ) { continue; } - const midiEvent * midi_event = parseData( c ); + parseData( c ); #else - const midiEvent * midi_event = parseData( m_midiDev.getch() ); + parseData( m_midiDev.getch() ); #endif - if( midi_event != NULL ) - { - processInEvent( *midi_event ); - } } } @@ -146,7 +142,7 @@ void midiOSS::run( void ) midiOSS::setupWidget::setupWidget( QWidget * _parent ) : - midiDevice::setupWidget( midiOSS::name(), _parent ) + midiRawClient::setupWidget( midiOSS::name(), _parent ) { m_device = new QLineEdit( midiOSS::probeDevice(), this ); m_device->setGeometry( 10, 20, 160, 20 ); diff --git a/src/midi/midi_port.cpp b/src/midi/midi_port.cpp new file mode 100644 index 000000000..32f478b9a --- /dev/null +++ b/src/midi/midi_port.cpp @@ -0,0 +1,85 @@ +/* + * midi_port.cpp - abstraction of MIDI-ports which are part of LMMS's MIDI- + * sequencing system + * + * Linux MultiMedia Studio + * Copyright (_c) 2004-2005 Tobias Doerffel + * This file partly contains code from Fluidsynth, Peter Hanappe + * + * 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 "midi_port.h" +#include "midi_client.h" + + + +midiPort::midiPort( midiClient * _mc, midiEventProcessor * _mep, + const QString & _name, modes _mode ) : + m_midiClient( _mc ), + m_midiEventProcessor( _mep ), + m_name( _name ), + m_mode( _mode ), + m_inputChannel( -1 ), + m_outputChannel( -1 ) +{ +} + + + + +midiPort::~midiPort() +{ +} + + + + +void midiPort::trySetName( const QString & _name ) +{ + setName( _name ); + m_midiClient->validatePortName( this ); +} + + + + +void midiPort::processInEvent( const midiEvent & _me, const midiTime & _time ) +{ + // mask event + if( ( mode() == INPUT || mode() == DUPLEX ) && + ( inputChannel() == _me.m_channel || inputChannel() == -1 ) ) + { + m_midiEventProcessor->processInEvent( _me, _time ); + } +} + + + + +void midiPort::processOutEvent( const midiEvent & _me, const midiTime & _time ) +{ + // mask event + if( ( mode() == OUTPUT || mode() == DUPLEX ) && + ( outputChannel() == _me.m_channel || outputChannel() == -1 ) ) + { + m_midiClient->processOutEvent( _me, _time, this ); + } +} + + diff --git a/src/widgets/knob.cpp b/src/widgets/knob.cpp index f840098b5..60abce2bb 100644 --- a/src/widgets/knob.cpp +++ b/src/widgets/knob.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #else @@ -50,6 +51,7 @@ #include #include #include +#include #define addSeparator insertSeparator @@ -62,7 +64,7 @@ #include "knob.h" #include "song_editor.h" -#include "midi_device.h" +/*#include "midi_client.h"*/ #include "embed.h" #include "spc_bg_hndl_widget.h" #include "config_mgr.h" @@ -91,12 +93,9 @@ knob::knob( int _knob_num, QWidget * _parent, const QString & _name ) : , _name.ascii() #endif ), - m_scrollMode( ScrNone ), m_mouseOffset( 0.0f ), - m_tracking( TRUE ), + m_buttonPressed( FALSE ), m_angle( 0.0f ), - m_oldAngle( 0.0f ), - m_nTurns( 0.0f ), m_knobNum( _knob_num ), m_hintTextBeforeValue( "" ), m_hintTextAfterValue( "" ), @@ -116,23 +115,14 @@ knob::knob( int _knob_num, QWidget * _parent, const QString & _name ) : #ifdef QT4 setAccessibleName( _name ); -#endif - setRange( 0.0, 100.0, 1.0 ); - -#ifdef QT4 m_knobPixmap = new QPixmap( embed::getIconPixmap( QString( "knob0" + QString::number( m_knobNum + 1 ) ).toAscii().constData() ) ); #else + setBackgroundMode( Qt::NoBackground ); m_knobPixmap = new QPixmap( embed::getIconPixmap( "knob0" + QString::number( m_knobNum + 1 ) ) ); #endif -#ifdef QT4 -// setAttribute( Qt::WA_NoBackground ); -#else - setBackgroundMode( Qt::NoBackground ); -#endif - - m_knobWidth = m_knobPixmap->width(); + setRange( 0.0f, 100.0f, 1.0f ); setFixedSize( m_knobPixmap->width(), m_knobPixmap->height() ); setTotalAngle( 270.0f ); @@ -146,12 +136,12 @@ knob::knob( int _knob_num, QWidget * _parent, const QString & _name ) : // Destructor knob::~knob() { - // make sure pointer to this knob isn't used anymore in active +/* // make sure pointer to this knob isn't used anymore in active // midi-device-class - if( mixer::inst()->getMIDIDevice()->pitchBendKnob() == this ) + if( mixer::inst()->getMIDIClient()->pitchBendKnob() == this ) { - mixer::inst()->getMIDIDevice()->setPitchBendKnob( NULL ); - } + mixer::inst()->getMIDIClient()->setPitchBendKnob( NULL ); + }*/ } @@ -251,10 +241,7 @@ void knob::valueChange( void ) { recalcAngle(); update(); - if( m_tracking ) - { - emit valueChanged( value() ); - } + emit valueChanged( value() ); } @@ -273,8 +260,7 @@ float knob::getValue( const QPoint & _p ) const float arc = atan2( -dx, dy ) * 180.0 / M_PI; float new_value = 0.5 * ( m_minValue + m_maxValue ) + - ( arc + m_nTurns * 360.0 ) * - ( m_maxValue - m_minValue ) / + arc * ( m_maxValue - m_minValue ) / m_totalAngle; const float oneTurn = tAbs( m_maxValue - m_minValue ) * @@ -294,17 +280,7 @@ float knob::getValue( const QPoint & _p ) } return( new_value ); } - - return( ( _p.y() - m_origMousePos.y() ) * m_step ); -} - - - - -void knob::getScrollMode( const QPoint &, int & _scroll_mode, int & _direction ) -{ - _scroll_mode = ScrMouse; - _direction = 0; + return( ( _p.y() - m_origMousePos.y() ) * m_pageSize ); } @@ -343,7 +319,6 @@ void knob::layoutKnob( bool _update_geometry ) void knob::paintEvent( QPaintEvent * _me ) { - // Use double-buffering QRect ur = _me->rect(); #ifndef QT4 if( ur.isValid() ) @@ -378,22 +353,18 @@ void knob::paintEvent( QPaintEvent * _me ) void knob::recalcAngle( void ) { - m_oldAngle = m_angle; - // // calculate the angle corresponding to the value // if( m_maxValue == m_minValue ) { m_angle = 0; - m_nTurns = 0; } else { m_angle = ( value() - 0.5 * ( m_minValue + m_maxValue ) ) / ( m_maxValue - m_minValue ) * m_totalAngle; - m_nTurns = floor( ( m_angle + 180.0 ) / 360.0 ); - m_angle = m_angle - m_nTurns * 360.0; + m_angle = static_cast( m_angle ) % 360; } } @@ -403,26 +374,18 @@ void knob::recalcAngle( void ) //! Mouse press event handler void knob::mousePressEvent( QMouseEvent * _me ) { - const QPoint & p = _me->pos(); - m_origMousePos = p; - - getScrollMode( p, m_scrollMode, m_direction ); - - switch( m_scrollMode ) - { - case ScrMouse: - m_mouseOffset = getValue( p ) - value(); - emit sliderPressed(); - break; - - default: - m_mouseOffset = 0; - m_direction = 0; - break; - } - if( _me->button() == Qt::LeftButton ) { + const QPoint & p = _me->pos(); + m_origMousePos = p; + + if( configManager::inst()->value( "knobs", + "classicalusability").toInt() ) + { + m_mouseOffset = getValue( p ) - value(); + } + emit sliderPressed(); + if( !configManager::inst()->value( "knobs", "classicalusability" ).toInt() ) { @@ -435,6 +398,11 @@ void knob::mousePressEvent( QMouseEvent * _me ) s_textFloat->move( mapTo( topLevelWidget(), QPoint( 0, 0 ) ) + QPoint( m_knobPixmap->width() + 2, 0 ) ); s_textFloat->show(); + m_buttonPressed = TRUE; + } + else if( _me->button() == Qt::MidButton ) + { + reset(); } } @@ -444,26 +412,21 @@ void knob::mousePressEvent( QMouseEvent * _me ) //! Mouse Move Event handler void knob::mouseMoveEvent( QMouseEvent * _me ) { - if( m_scrollMode == ScrMouse ) + if( m_buttonPressed == TRUE ) { setPosition( _me->pos() ); - if( value() != m_prevValue ) - { - emit sliderMoved( value() ); - if( !configManager::inst()->value( "knobs", + emit sliderMoved( value() ); + if( !configManager::inst()->value( "knobs", "classicalusability").toInt() ) - { - QCursor::setPos( mapToGlobal( - m_origMousePos ) ); - } + { + QCursor::setPos( mapToGlobal( m_origMousePos ) ); } + songEditor::inst()->setModified(); } - songEditor::inst()->setModified(); s_textFloat->setText( m_hintTextBeforeValue + QString::number( value() ) + m_hintTextAfterValue ); - } @@ -472,30 +435,14 @@ void knob::mouseMoveEvent( QMouseEvent * _me ) //! Mouse Release Event handler void knob::mouseReleaseEvent( QMouseEvent * /* _me*/ ) { - if( m_scrollMode != ScrNone ) + if( m_buttonPressed ) { - m_scrollMode = ScrNone; + m_buttonPressed = TRUE; buttonReleased(); } - switch( m_scrollMode ) - { - case ScrMouse: - //setPosition( _me->pos() ); - m_direction = 0; - m_mouseOffset = 0; - emit sliderReleased(); - break; - - case ScrDirect: - //setPosition( _me->pos() ); - m_direction = 0; - m_mouseOffset = 0; - break; - - default: - break; - } + m_mouseOffset = 0; + emit sliderReleased(); if( !configManager::inst()->value( "knobs", "classicalusability" ).toInt() ) @@ -509,15 +456,23 @@ void knob::mouseReleaseEvent( QMouseEvent * /* _me*/ ) -//! Qt wheel event -void knob::wheelEvent( QWheelEvent * _me ) +void knob::mouseDoubleClickEvent( QMouseEvent * ) { - _me->accept(); - const int inc = _me->delta() / WHEEL_DELTA; - incPages( inc ); + enterValue(); +} + + + +//! Qt wheel event +void knob::wheelEvent( QWheelEvent * _we ) +{ + _we->accept(); + const int inc = _we->delta() / WHEEL_DELTA; + incValue( inc ); songEditor::inst()->setModified(); + s_textFloat->reparent( this ); s_textFloat->setText( m_hintTextBeforeValue + QString::number( value() ) + @@ -538,7 +493,7 @@ void knob::wheelEvent( QWheelEvent * _me ) //! Emits a valueChanged() signal if necessary void knob::buttonReleased( void ) { - if( ( !m_tracking ) || ( value() != m_prevValue ) ) + if( value() != m_prevValue ) { emit valueChanged( value() ); } @@ -552,26 +507,17 @@ void knob::setPosition( const QPoint & _p ) if( configManager::inst()->value( "knobs", "classicalusability" ).toInt() ) { - setNewValue( getValue( _p ) - m_mouseOffset, 1 ); + setValue( getValue( _p ) - m_mouseOffset ); } else { - setNewValue( m_value - getValue( _p ), 1 ); + setValue( m_value - getValue( _p ) ); } } -void knob::setTracking( bool _enable ) -{ - m_tracking = _enable; -} - - - - - void knob::setValue( float _val, bool _is_init_value ) { if( _is_init_value ) @@ -579,7 +525,7 @@ void knob::setValue( float _val, bool _is_init_value ) m_initValue = _val; } - setNewValue( _val, 0 ); + setNewValue( _val, TRUE ); } @@ -587,7 +533,7 @@ void knob::setValue( float _val, bool _is_init_value ) void knob::fitValue( float _val ) { - setNewValue( _val, 1 ); + setValue( _val ); } @@ -595,7 +541,7 @@ void knob::fitValue( float _val ) void knob::incValue( int _steps ) { - setNewValue( m_value + float( _steps ) * m_step, 1 ); + setValue( m_value + float( _steps ) * m_step ); } @@ -622,13 +568,14 @@ void knob::setRange( float _vmin, float _vmax, float _vstep, int _page_size ) // /* m_pageSize = tLimit( pageSize, 0, int( tAbs( ( m_maxValue - m_minValue ) / m_step ) ) ); */ - m_pageSize = tMax( ( m_maxValue - m_minValue ) / 40.0f, _vstep ); + m_pageSize = tMax( ( m_maxValue - m_minValue ) / 100.0f, + m_step ); // // If the value lies out of the range, it // will be changed. Note that it will not be adjusted to // the new step width. - setNewValue( m_value, 0 ); + setNewValue( m_value, FALSE ); // call notifier after the step width has been // adjusted. @@ -641,7 +588,7 @@ void knob::setRange( float _vmin, float _vmax, float _vstep, int _page_size ) -void knob::setNewValue( float _x, int _align ) +void knob::setNewValue( float _x, bool _align ) { m_prevValue = m_value; @@ -768,6 +715,14 @@ void knob::contextMenuEvent( QContextMenuEvent * ) void knob::reset( void ) { setValue( m_initValue ); + songEditor::inst()->setModified(); + s_textFloat->reparent( this ); + s_textFloat->setText( m_hintTextBeforeValue + + QString::number( value() ) + + m_hintTextAfterValue ); + s_textFloat->move( mapTo( topLevelWidget(), QPoint( 0, 0 ) ) + + QPoint( m_knobPixmap->width() + 2, 0 ) ); + s_textFloat->setVisibilityTimeOut( 1000 ); } @@ -784,6 +739,32 @@ void knob::copyValue( void ) void knob::pasteValue( void ) { setValue( s_copiedValue ); + songEditor::inst()->setModified(); + s_textFloat->reparent( this ); + s_textFloat->setText( m_hintTextBeforeValue + + QString::number( value() ) + + m_hintTextAfterValue ); + s_textFloat->move( mapTo( topLevelWidget(), QPoint( 0, 0 ) ) + + QPoint( m_knobPixmap->width() + 2, 0 ) ); + s_textFloat->setVisibilityTimeOut( 1000 ); +} + + + + +void knob::enterValue( void ) +{ + bool ok; + float new_val = QInputDialog::getDouble( accessibleName(), + tr( "Please enter a new value between " + "%1 and %2:" ).arg( + minValue() ).arg( maxValue() ), + value(), minValue(), maxValue(), + 4, &ok, this ); + if( ok ) + { + setValue( new_val ); + } } @@ -791,7 +772,7 @@ void knob::pasteValue( void ) void knob::connectToMidiDevice( void ) { - mixer::inst()->getMIDIDevice()->setPitchBendKnob( this ); + //mixer::inst()->getMIDIDevice()->setPitchBendKnob( this ); } diff --git a/src/widgets/lcd_spinbox.cpp b/src/widgets/lcd_spinbox.cpp index ca6099ae5..8e626631f 100644 --- a/src/widgets/lcd_spinbox.cpp +++ b/src/widgets/lcd_spinbox.cpp @@ -58,15 +58,8 @@ lcdSpinBox::lcdSpinBox( int _min, int _max, int _num_digits, m_number->setFrameShape( QFrame::Panel ); m_number->setFrameShadow( QFrame::Sunken ); m_number->setSegmentStyle( QLCDNumber::Flat ); -#ifdef QT4 - QPalette pal = m_number->palette(); - pal.setColor( QPalette::Background, QColor( 32, 32, 32 ) ); - pal.setColor( QPalette::Foreground, QColor( 255, 180, 0 ) ); - m_number->setPalette( pal ); -#else - m_number->setPaletteBackgroundColor( QColor( 32, 32, 32 ) ); - m_number->setPaletteForegroundColor( QColor( 255, 180, 0 ) ); -#endif + setEnabled( TRUE ); + // value is automatically limited to given range setValue( 0 ); @@ -95,12 +88,15 @@ void lcdSpinBox::setValue( int _value ) { _value = ( ( tLimit( _value, m_minValue, m_maxValue ) - m_minValue ) / m_step ) * m_step + m_minValue; - QString s = QString::number( _value ); - while( (int) s.length() < m_number->numDigits() ) + QString s = m_textForValue[_value]; + if( s == "" ) { - s = "0" + s; + s = QString::number( _value ); + while( (int) s.length() < m_number->numDigits() ) + { + s = "0" + s; + } } - m_number->display( s ); } @@ -127,6 +123,28 @@ void lcdSpinBox::setLabel( const QString & _txt ) +void lcdSpinBox::setEnabled( bool _on ) +{ + QColor fg( 255, 180, 0 ); + if( _on == FALSE ) + { + fg = QColor( 160, 160, 160 ); + } +#ifdef QT4 + QPalette pal = m_number->palette(); + pal.setColor( QPalette::Background, QColor( 32, 32, 32 ) ); + pal.setColor( QPalette::Foreground, fg ); + m_number->setPalette( pal ); +#else + m_number->setPaletteBackgroundColor( QColor( 32, 32, 32 ) ); + m_number->setPaletteForegroundColor( fg ); +#endif + QWidget::setEnabled( _on ); +} + + + + void lcdSpinBox::mousePressEvent( QMouseEvent * _me ) { if( _me->button() == Qt::LeftButton && _me->y() < m_number->height() ) @@ -138,6 +156,7 @@ void lcdSpinBox::mousePressEvent( QMouseEvent * _me ) + void lcdSpinBox::mouseMoveEvent( QMouseEvent * _me ) { if( _me->modifiers() == Qt::LeftButton ) diff --git a/src/widgets/led_checkbox.cpp b/src/widgets/led_checkbox.cpp index f07752cc1..47a32228c 100644 --- a/src/widgets/led_checkbox.cpp +++ b/src/widgets/led_checkbox.cpp @@ -117,6 +117,10 @@ void ledCheckBox::setChecked( bool _on ) { toggle(); } + else + { + emit( toggled( m_checked ) ); + } } diff --git a/src/widgets/tempo_sync_knob.cpp b/src/widgets/tempo_sync_knob.cpp index 5778f0a34..b53730c81 100644 --- a/src/widgets/tempo_sync_knob.cpp +++ b/src/widgets/tempo_sync_knob.cpp @@ -49,22 +49,15 @@ #endif -#ifndef __USE_XOPEN -#define __USE_XOPEN -#endif #include "tempo_sync_knob.h" #include "song_editor.h" -#include "midi_device.h" #include "embed.h" #include "tooltip.h" #include "config_mgr.h" #include "text_float.h" -const int WHEEL_DELTA = 120; - - tempoSyncKnob::tempoSyncKnob( int _knob_num, QWidget * _parent, const QString & _name, @@ -171,59 +164,19 @@ void tempoSyncKnob::contextMenuEvent( QContextMenuEvent * ) void tempoSyncKnob::mouseMoveEvent( QMouseEvent * _me ) { - if( m_scrollMode == ScrMouse ) - { - m_tempoSyncMode = NO_SYNC; - calculateTempoSyncTime( songEditor::inst()->getBPM() ); - - setPosition( _me->pos() ); - if( value() != m_prevValue ) - { - emit sliderMoved( value() ); - } - if( !configManager::inst()->value( "knobs", "classicalusability" - ).toInt() ) - { - QCursor::setPos( mapToGlobal( m_origMousePos ) ); - } - } - songEditor::inst()->setModified(); - - s_textFloat->setText( m_hintTextBeforeValue + - QString::number( value() ) + - m_hintTextAfterValue ); - + m_tempoSyncMode = NO_SYNC; + calculateTempoSyncTime( songEditor::inst()->getBPM() ); + knob::mouseMoveEvent( _me ); } -void tempoSyncKnob::wheelEvent( QWheelEvent * _me ) +void tempoSyncKnob::wheelEvent( QWheelEvent * _we ) { - _me->accept(); - const int inc = _me->delta() / WHEEL_DELTA; - incPages( inc ); - + knob::wheelEvent( _we ); m_tempoSyncMode = NO_SYNC; calculateTempoSyncTime( songEditor::inst()->getBPM() ); - - songEditor::inst()->setModified(); - - s_textFloat->reparent( this ); - s_textFloat->setText( m_hintTextBeforeValue + - QString::number( value() ) + - m_hintTextAfterValue ); - s_textFloat->move( mapTo( topLevelWidget(), QPoint( 0, 0 ) ) + - QPoint( m_knobPixmap->width() + 2, 0 ) ); - s_textFloat->setVisibilityTimeOut( 1000 ); - - toolTip::add( this, m_hintTextBeforeValue+QString::number( value() ) + - m_hintTextAfterValue ); - - if( value() != m_prevValue ) - { - emit sliderMoved( value() ); - } } @@ -283,7 +236,7 @@ void tempoSyncKnob::calculateTempoSyncTime( int _bpm ) conversionFactor = 8.0; break; default: - printf( "arpAndChordsTabWidget::calculateTempoSyncTime: invalid tempoSyncMode" ); + printf( "tempoSyncKnob::calculateTempoSyncTime: invalid tempoSyncMode" ); break; } setValue( 60000.0 / ( _bpm * conversionFactor * m_scale ),