diff --git a/ChangeLog b/ChangeLog index 0bc988369..ea6a3d1c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,53 @@ +2005-03-26 Danny McRae + + * plugins/vibed/: + added "Vibed"-plugin, a powerful combination of PluckedStringSynth and + BitInvader that's capable of producing a surprisingly wide range of + sounds + +2006-03-26 Tobias Doerffel + + * src/lib/journalling_object.cpp: + in journallingObject::saveState(): append new dom-element to parent + before calling saveSettings() as some implementations of it depend on + knowing something about it's parent (e.g. whether it's the clipboard + etc.) - fixes several bugs related to clipboard-usage and drag'n'drop + +2006-03-25 Tobias Doerffel + + * include/instrument_track.h: + * src/core/preset_preview_play_handle.cpp: + disable journalling for all objects involved in preset-preview + + * src/tracks/instrument_track.cpp: + when saving preset, create new node below content-node of project, as + the behaviour of saveTrackSpecificSettings() was changed some days + before + + * src/lib/mmp.cpp: + bugfixes concerning project type-naming + + * src/core/arp_and_chords_tab_widget.cpp: + bugfixes concerning loading/saving settings + +2006-03-24 Tobias Doerffel + + * src/core/track.cpp: + in trackContentWidget::undoStep(): cast journalling object to + track-content-object and close it instead of deleting it - fixes crash + after undo of TCO-add + 2006-03-23 Tobias Doerffel + * plugins/organic/organic.cpp: + * plugins/organic/randomise_pressed.png: + added pixmap for visual feedback when pressing randomise-button + + * plugins/midi_import/midi_import.cpp: + removed attribute FASTCALL from midiImport::tryImport() for not + crashing LMMS, as tryImport() is not defined as FASTCALL in + importFilter-API + * src/core/export_project_dialog.cpp: fixed bug causing LMMS to crash when trying to export a project diff --git a/TODO b/TODO index 7bf82ef55..c234681b1 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +- when recording MIDI-events, add switch to disable recording of velocity +- integrated sample-browser in context-menu of sample-track/-tco - font-size-scaling-coefficient in setup-dialog - make note able of journalling - before calling undoStep/redoStep from journallingObject, save journalling-state-context and disabled journalling, restore afterwards @@ -5,7 +7,6 @@ - undo/redo-support in note/track etc. - save tco-settings in trackContentWidget::saveSettings() etc. instead of track::... - restore stacking-order of windows when loading project -- fix MIDI-import-filter - bristol-bindings - resample sample-track-tcos when using hq-mode - add support for panes-interface (like blender) (instead of MDI etc.) @@ -25,7 +26,6 @@ - level-meters in output-graph and channel-track - do not skip samples because of rounding-errors when resampling in src/lib/sample_buffer.cpp - MIDI-program/MIDI-mapping/process program-/channel-change-events from MIDI-files -- add note-len- and note-alignment-selectbox to piano-roll - DSSI-support - 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 diff --git a/configure.in b/configure.in index f06381aa3..db9c10e2d 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.4-cvs20060323, tobydox/at/users/dot/sourceforge/dot/net) -AM_INIT_AUTOMAKE(lmms, 0.1.4-cvs20060323) +AC_INIT(lmms, 0.1.4-cvs20060326, tobydox/at/users/dot/sourceforge/dot/net) +AM_INIT_AUTOMAKE(lmms, 0.1.4-cvs20060326) AM_CONFIG_HEADER(config.h) @@ -470,6 +470,7 @@ AC_CONFIG_FILES([Makefile plugins/plucked_string_synth/Makefile plugins/triple_oscillator/Makefile plugins/vestige/Makefile + plugins/vibed/Makefile lmms.spec]) AC_OUTPUT diff --git a/data/presets/TripleOscillator/AnalogTimes2.cs.xml b/data/presets/TripleOscillator/AnalogTimes2.cs.xml new file mode 100644 index 000000000..744848728 --- /dev/null +++ b/data/presets/TripleOscillator/AnalogTimes2.cs.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/include/instrument_track.h b/include/instrument_track.h index fd8803214..c3b73bb06 100755 --- a/include/instrument_track.h +++ b/include/instrument_track.h @@ -155,6 +155,9 @@ public: virtual void FASTCALL loadTrackSpecificSettings( const QDomElement & _this ); + using track::setJournalling; + + // load instrument whose name matches given one instrument * FASTCALL loadInstrument( const QString & _instrument_name ); diff --git a/include/note_play_handle.h b/include/note_play_handle.h index 7ab6a2a37..ca58047e2 100644 --- a/include/note_play_handle.h +++ b/include/note_play_handle.h @@ -160,7 +160,7 @@ public: int index( void ) const; // note-play-handles belonging to given channel - static constNotePlayHandleVector nphsOfChannelTrack( + static constNotePlayHandleVector nphsOfInstrumentTrack( const instrumentTrack * _ct ); // return whether given note-play-handle is equal to *this diff --git a/include/preset_preview_play_handle.h b/include/preset_preview_play_handle.h index d0395738d..c3d84ebba 100644 --- a/include/preset_preview_play_handle.h +++ b/include/preset_preview_play_handle.h @@ -60,7 +60,7 @@ public: virtual bool done( void ) const; static void cleanUp( engine * _engine ); - static constNotePlayHandleVector nphsOfChannelTrack( + static constNotePlayHandleVector nphsOfInstrumentTrack( const instrumentTrack * _ct ); private: diff --git a/include/project_journal.h b/include/project_journal.h index 1ac494f23..50731fa20 100755 --- a/include/project_journal.h +++ b/include/project_journal.h @@ -90,7 +90,13 @@ public: // journalling information about the ID get's lost void forgetAboutID( const jo_id_t _id ); - void clear( void ); + void clearJournal( void ) + { + m_journalEntries.clear(); + m_currentJournalEntry = m_journalEntries.end(); + } + + void clearInvalidJournallingObjects( void ); journallingObject * getJournallingObject( const jo_id_t _id ) { diff --git a/include/track.h b/include/track.h index eb555012a..5c710f690 100644 --- a/include/track.h +++ b/include/track.h @@ -129,8 +129,8 @@ protected: void setAutoResizeEnabled( bool _e = FALSE ); float pixelsPerTact( void ); - virtual void undoStep( journalEntry & _edit_step ); - virtual void redoStep( journalEntry & _edit_step ); + virtual void undoStep( journalEntry & _je ); + virtual void redoStep( journalEntry & _je ); protected slots: @@ -210,8 +210,8 @@ protected: return( "trackcontentwidget" ); } - virtual void undoStep( journalEntry & _edit_step ); - virtual void redoStep( journalEntry & _edit_step ); + virtual void undoStep( journalEntry & _je ); + virtual void redoStep( journalEntry & _je ); private: @@ -277,7 +277,7 @@ private: // actual widget shown in trackContainer -class trackWidget : public QWidget +class trackWidget : public QWidget, public journallingObject { Q_OBJECT public: @@ -338,6 +338,15 @@ public slots: protected: + virtual void undoStep( journalEntry & _je ); + virtual void redoStep( journalEntry & _je ); + + virtual QString nodeName( void ) const + { + return( "trackwidget" ); + } + + virtual void dragEnterEvent( QDragEnterEvent * _dee ); virtual void dropEvent( QDropEvent * _de ); virtual void mousePressEvent( QMouseEvent * _me ); diff --git a/include/track_container.h b/include/track_container.h index ee1f10e65..6a0891c6a 100644 --- a/include/track_container.h +++ b/include/track_container.h @@ -129,6 +129,9 @@ public: protected: + virtual void undoStep( journalEntry & _je ); + virtual void redoStep( journalEntry & _je ); + virtual void dragEnterEvent( QDragEnterEvent * _dee ); virtual void dropEvent( QDropEvent * _de ); virtual void mousePressEvent( QMouseEvent * _me ); @@ -152,6 +155,10 @@ protected slots: private: + enum actions + { + ADD_TRACK, REMOVE_TRACK + } ; class scrollArea : public QScrollArea { diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 38b13efb8..50936ae95 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -2,5 +2,5 @@ if VST_SUPPORT VESTIGE_SUBDIR=vestige endif -SUBDIRS = audio_file_processor bit_invader midi_import organic plucked_string_synth triple_oscillator $(VESTIGE_SUBDIR) +SUBDIRS = audio_file_processor bit_invader midi_import organic plucked_string_synth triple_oscillator $(VESTIGE_SUBDIR) vibed diff --git a/plugins/midi_import/midi_import.cpp b/plugins/midi_import/midi_import.cpp index 85eb91f15..7fc172b77 100644 --- a/plugins/midi_import/midi_import.cpp +++ b/plugins/midi_import/midi_import.cpp @@ -85,7 +85,7 @@ midiImport::~midiImport() -bool FASTCALL midiImport::tryImport( trackContainer * _tc ) +bool midiImport::tryImport( trackContainer * _tc ) { if( openFile() == FALSE ) { diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index 7e02726ae..d58dabdf1 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -184,7 +184,7 @@ organicInstrument::organicInstrument( instrumentTrack * _channel_track ) : m_randBtn = new pixmapButton( this, eng() ); m_randBtn->move( 100, 200 ); m_randBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( - "randomise" ) ); + "randomise_pressed" ) ); m_randBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "randomise" ) ); //m_randBtn->setMask( QBitmap( PLUGIN_NAME::getIconPixmap( "btn_mask" ). diff --git a/plugins/organic/randomise_pressed.png b/plugins/organic/randomise_pressed.png new file mode 100644 index 000000000..e3a7470d5 Binary files /dev/null and b/plugins/organic/randomise_pressed.png differ diff --git a/plugins/vibed/Makefile.am b/plugins/vibed/Makefile.am new file mode 100644 index 000000000..d266d4810 --- /dev/null +++ b/plugins/vibed/Makefile.am @@ -0,0 +1,44 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src/lib -I. + + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) -DPLUGIN_NAME="vibedstrings" + + +%.moc: ./%.h + $(MOC) -o $@ $< + + +MOC_FILES = ./vibed.moc ./graph.moc ./impulse_editor.moc ./nine_button_selector.moc + +BUILT_SOURCES = $(MOC_FILES) ./embedded_resources.h +EMBEDDED_RESOURCES = $(wildcard *png) + +./embedded_resources.h: $(EMBEDDED_RESOURCES) + $(top_builddir)/buildtools/bin2res $(EMBEDDED_RESOURCES) > $@ + +EXTRA_DIST = $(EMBEDDED_RESOURCES) + + +CLEANFILES = $(MOC_FILES) ./embedded_resources.h + + + +pkglib_LTLIBRARIES= libvibedstrings.la + +libvibedstrings_la_SOURCES = vibed.cpp \ + vibed.h \ + graph.cpp \ + graph.h \ + vibrating_string.cpp \ + vibrating_string.h \ + string_container.cpp \ + string_container.h \ + impulse_editor.cpp \ + impulse_editor.h \ + nine_button_selector.cpp \ + nine_button_selector.h + +$(libvibedstrings_la_SOURCES): ./embedded_resources.h diff --git a/plugins/vibed/artwork.png b/plugins/vibed/artwork.png new file mode 100644 index 000000000..1bd60dddb Binary files /dev/null and b/plugins/vibed/artwork.png differ diff --git a/plugins/vibed/button_-1_off.png b/plugins/vibed/button_-1_off.png new file mode 100644 index 000000000..7c5c23aa1 Binary files /dev/null and b/plugins/vibed/button_-1_off.png differ diff --git a/plugins/vibed/button_-1_on.png b/plugins/vibed/button_-1_on.png new file mode 100644 index 000000000..02e237796 Binary files /dev/null and b/plugins/vibed/button_-1_on.png differ diff --git a/plugins/vibed/button_-2_off.png b/plugins/vibed/button_-2_off.png new file mode 100644 index 000000000..13a7e2058 Binary files /dev/null and b/plugins/vibed/button_-2_off.png differ diff --git a/plugins/vibed/button_-2_on.png b/plugins/vibed/button_-2_on.png new file mode 100644 index 000000000..a5cb24de3 Binary files /dev/null and b/plugins/vibed/button_-2_on.png differ diff --git a/plugins/vibed/button_1_off.png b/plugins/vibed/button_1_off.png new file mode 100644 index 000000000..5584a68d1 Binary files /dev/null and b/plugins/vibed/button_1_off.png differ diff --git a/plugins/vibed/button_1_on.png b/plugins/vibed/button_1_on.png new file mode 100644 index 000000000..655991fce Binary files /dev/null and b/plugins/vibed/button_1_on.png differ diff --git a/plugins/vibed/button_2_off.png b/plugins/vibed/button_2_off.png new file mode 100644 index 000000000..558ea6b29 Binary files /dev/null and b/plugins/vibed/button_2_off.png differ diff --git a/plugins/vibed/button_2_on.png b/plugins/vibed/button_2_on.png new file mode 100644 index 000000000..0c5f0d13f Binary files /dev/null and b/plugins/vibed/button_2_on.png differ diff --git a/plugins/vibed/button_3_off.png b/plugins/vibed/button_3_off.png new file mode 100644 index 000000000..83a815912 Binary files /dev/null and b/plugins/vibed/button_3_off.png differ diff --git a/plugins/vibed/button_3_on.png b/plugins/vibed/button_3_on.png new file mode 100644 index 000000000..b4350c265 Binary files /dev/null and b/plugins/vibed/button_3_on.png differ diff --git a/plugins/vibed/button_4_off.png b/plugins/vibed/button_4_off.png new file mode 100644 index 000000000..16b2900bf Binary files /dev/null and b/plugins/vibed/button_4_off.png differ diff --git a/plugins/vibed/button_4_on.png b/plugins/vibed/button_4_on.png new file mode 100644 index 000000000..5851dd7e9 Binary files /dev/null and b/plugins/vibed/button_4_on.png differ diff --git a/plugins/vibed/button_5_off.png b/plugins/vibed/button_5_off.png new file mode 100644 index 000000000..a37a54736 Binary files /dev/null and b/plugins/vibed/button_5_off.png differ diff --git a/plugins/vibed/button_5_on.png b/plugins/vibed/button_5_on.png new file mode 100644 index 000000000..b43b1188b Binary files /dev/null and b/plugins/vibed/button_5_on.png differ diff --git a/plugins/vibed/button_6_off.png b/plugins/vibed/button_6_off.png new file mode 100644 index 000000000..874c9531e Binary files /dev/null and b/plugins/vibed/button_6_off.png differ diff --git a/plugins/vibed/button_6_on.png b/plugins/vibed/button_6_on.png new file mode 100644 index 000000000..3ee714928 Binary files /dev/null and b/plugins/vibed/button_6_on.png differ diff --git a/plugins/vibed/button_7_off.png b/plugins/vibed/button_7_off.png new file mode 100644 index 000000000..1994efc7e Binary files /dev/null and b/plugins/vibed/button_7_off.png differ diff --git a/plugins/vibed/button_7_on.png b/plugins/vibed/button_7_on.png new file mode 100644 index 000000000..65e331347 Binary files /dev/null and b/plugins/vibed/button_7_on.png differ diff --git a/plugins/vibed/button_8_off.png b/plugins/vibed/button_8_off.png new file mode 100644 index 000000000..76818e8e7 Binary files /dev/null and b/plugins/vibed/button_8_off.png differ diff --git a/plugins/vibed/button_8_on.png b/plugins/vibed/button_8_on.png new file mode 100644 index 000000000..370ad8208 Binary files /dev/null and b/plugins/vibed/button_8_on.png differ diff --git a/plugins/vibed/button_9_off.png b/plugins/vibed/button_9_off.png new file mode 100644 index 000000000..2c71abff8 Binary files /dev/null and b/plugins/vibed/button_9_off.png differ diff --git a/plugins/vibed/button_9_on.png b/plugins/vibed/button_9_on.png new file mode 100644 index 000000000..629d99964 Binary files /dev/null and b/plugins/vibed/button_9_on.png differ diff --git a/plugins/vibed/button_down.png b/plugins/vibed/button_down.png new file mode 100644 index 000000000..198f8464a Binary files /dev/null and b/plugins/vibed/button_down.png differ diff --git a/plugins/vibed/button_f_off.png b/plugins/vibed/button_f_off.png new file mode 100644 index 000000000..203cd8aed Binary files /dev/null and b/plugins/vibed/button_f_off.png differ diff --git a/plugins/vibed/button_f_on.png b/plugins/vibed/button_f_on.png new file mode 100644 index 000000000..125ddb3ce Binary files /dev/null and b/plugins/vibed/button_f_on.png differ diff --git a/plugins/vibed/button_up.png b/plugins/vibed/button_up.png new file mode 100644 index 000000000..0a517f508 Binary files /dev/null and b/plugins/vibed/button_up.png differ diff --git a/plugins/vibed/graph.cpp b/plugins/vibed/graph.cpp new file mode 100644 index 000000000..f310ae426 --- /dev/null +++ b/plugins/vibed/graph.cpp @@ -0,0 +1,265 @@ +/* + * graph.cpp - a QT widget for displaying and manipulating waveforms + * + * Copyright (c) 2006 Andreas Brandmaier + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * 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 +#include + +#endif + + +#include "graph.h" +#include "string_pair_drag.h" +#include "sample_buffer.h" +#include +#include + +using namespace std; + + + +graph::graph( QWidget * _parent, engine * _engine ) : + QWidget( _parent ), + engineObject( _engine ) +{ + m_mouseDown = false; + + setFixedSize( 132, 104 ); + + setAcceptDrops( TRUE ); + +#ifndef QT4 + setBackgroundMode( NoBackground ); +#endif + +} + + + + +graph::~graph() +{ +} + +void graph::setForeground( const QPixmap &_pixmap ) +{ + m_foreground = _pixmap; +} + +void graph::setSamplePointer( float * _pointer, int _length ) +{ + samplePointer = _pointer; + sampleLength = _length; + update(); +} + +void graph::loadSampleFromFile( const QString & _filename ) +{ + // zero sample_shape + for (int i = 0; i < sampleLength; i++) + { + samplePointer[i] = 0; + } + + // load user shape + sampleBuffer buffer( eng(), _filename ); + + // copy buffer data + sampleLength = min( sampleLength, static_cast(buffer.frames()) ); + for ( int i = 0; i < sampleLength; i++ ) + { + samplePointer[i] = (float)*buffer.data()[i]; + } + +} + +void graph::mouseMoveEvent ( QMouseEvent * _me ) +{ + // get position + int x = _me->x(); + int y = _me->y(); + + + // avoid mouse leaps + int diff = x - m_lastCursorX; + + if( diff >= 1 ) + { + x = m_lastCursorX + 1; + } + else if( diff <= 1 ) + { + x = m_lastCursorX - 1; + } + else + { + x = m_lastCursorX; + } + + changeSampleAt( x, y ); + + // update mouse + m_lastCursorX = x; + +} + +void graph::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton ) + { + // toggle mouse state + m_mouseDown = true; + + // get position + int x = _me->x(); + int y = _me->y(); + + changeSampleAt( x,y ); + + // toggle mouse state + m_mouseDown = true; +#ifndef QT3 + setCursor( Qt::BlankCursor ); +#else + setCursor( QCursor::BlankCursor ); +#endif + m_lastCursorX = x; + } +} + +void graph::changeSampleAt(int _x, int _y) +{ + // consider border of background image + _x -= 2; + _y -= 2; + + // boundary check + if (_x < 0) { return; } + if (_x > sampleLength) { return; } + if (_y < 0) { return; } + if (_y >= 100) { return; } + _y = 100 - _y; + + // change sample shape + samplePointer[_x] = (_y-50.0)/50.0; + emit sampleChanged(); + + +} + +void graph::mouseReleaseEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton ) + { + // toggle mouse state + m_mouseDown = false; +#ifndef QT3 + setCursor( Qt::ArrowCursor ); +#else + setCursor( QCursor::ArrowCursor ); +#endif + update(); + } +} + + + +void graph::paintEvent( QPaintEvent * ) +{ + +#ifdef QT4 + QPainter p( this ); +#else + QPixmap draw_pm( rect().size() ); + draw_pm.fill( this, rect().topLeft() ); + + QPainter p( &draw_pm, this ); +#endif + + p.setPen( QColor( 0xFF, 0xAA, 0x00 ) ); + + p.drawLine( 1+sampleLength, 2, 1+sampleLength, 102); + + float xscale = 128.0 / sampleLength; + + for (int i=0; i < sampleLength-1; i++) + { + p.drawLine(2+static_cast(i*xscale), + 2+static_cast(-samplePointer[i]*50) + 50, + 2+static_cast((i+1)*xscale), + 2+static_cast(-samplePointer[i+1]*50 + 50) + ); + } + + // draw Pointer + if (m_mouseDown) { + QPoint cursor = mapFromGlobal( QCursor::pos() ); + p.setPen( QColor( 0xAA, 0xFF, 0x00 ) ); + p.drawLine( 2, cursor.y(), 130, cursor.y() ); + p.drawLine( cursor.x(), 2, cursor.x(), 102 ); + } + p.drawPixmap( 0, 0, m_foreground ); + +#ifndef QT4 + // and blit all the drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif + +} + + +void graph::dropEvent( QDropEvent * _de ) +{ + QString type = stringPairDrag::decodeKey( _de ); + QString value = stringPairDrag::decodeValue( _de ); + + if( type == "samplefile" ) + { + loadSampleFromFile( value ); + _de->accept(); + } +} + +void graph::dragEnterEvent( QDragEnterEvent * _dee ) +{ + if( stringPairDrag::processDragEnterEvent( _dee, + QString( "samplefile" ) ) == FALSE ) + { + _dee->ignore(); + } +} + + +#include "graph.moc" diff --git a/plugins/vibed/graph.h b/plugins/vibed/graph.h new file mode 100644 index 000000000..6976c3452 --- /dev/null +++ b/plugins/vibed/graph.h @@ -0,0 +1,87 @@ +/* + * graph.h - a QT widget for displaying and manipulating waveforms + * + * Copyright (c) 2006 Andreas Brandmaier + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * 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 _GRAPH_H +#define _GRAPH_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "engine.h" + + +class graph : public QWidget, public engineObject +{ + Q_OBJECT +public: + graph( QWidget * _parent, engine * _engine ); + virtual ~graph(); + + void setSamplePointer( float * pointer, int length ); + void setForeground( const QPixmap & _pixmap ); + void loadSampleFromFile( const QString & _filename ); + +signals: + void sampleSizeChanged( float f ); + void sampleChanged( void ); + +protected: + virtual void paintEvent( QPaintEvent * _pe ); + virtual void dropEvent( QDropEvent * _de ); + virtual void dragEnterEvent( QDragEnterEvent * _dee ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + +private: + + void changeSampleAt(int _x, int _y); + + QPixmap m_foreground; + + + float *samplePointer; + int sampleLength; + + bool m_mouseDown; + int m_lastCursorX; + +} ; + +#endif diff --git a/plugins/vibed/impulse_editor.cpp b/plugins/vibed/impulse_editor.cpp new file mode 100644 index 000000000..fa172b5ae --- /dev/null +++ b/plugins/vibed/impulse_editor.cpp @@ -0,0 +1,492 @@ +/* + * impulse_editor.cpp - graphic waveform editor + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include + +#endif + +#include "impulse_editor.h" +#include "vibed.h" +#include "tooltip.h" +#include "oscillator.h" +#include "song_editor.h" + + +impulseEditor::impulseEditor( QWidget * _parent, int _x, int _y, + engine * _engine, Uint32 _len ) : + QWidget( _parent, "impulseEditor" ), + engineObject( _engine ), + m_sampleLength( _len ), + m_normalizeFactor( 1.0f ), + m_forward( TRUE ) +{ + setFixedSize( 153, 124 ); + m_base = QPixmap::grabWidget( _parent, _x, _y ); + setPaletteBackgroundPixmap( m_base ); + + m_graph = new graph( this, eng() ); + m_graph->setForeground( PLUGIN_NAME::getIconPixmap( "wavegraph4" ) ); + m_graph->move( 0, 0 ); + m_graph->setCursor( QCursor( Qt::CrossCursor ) ); + toolTip::add( m_graph, tr ( "Draw your own waveform here " + "by dragging your mouse onto this graph" ) ); + + connect( m_graph, SIGNAL ( sampleChanged( void ) ), + this, SLOT ( sampleChanged( void ) ) ); + + m_sinWaveBtn = new pixmapButton( this, eng() ); + m_sinWaveBtn->move( 136, 3 ); + m_sinWaveBtn->setActiveGraphic( embed::getIconPixmap( + "sin_wave_active" ) ); + m_sinWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "sin_wave_inactive" ) ); + m_sinWaveBtn->setChecked( TRUE ); + toolTip::add( m_sinWaveBtn, + tr( "Click here if you want a sine-wave for " + "current oscillator." ) ); + connect( m_sinWaveBtn, SIGNAL (clicked ( void ) ), + this, SLOT ( sinWaveClicked( void ) ) ); + + + m_triangleWaveBtn = new pixmapButton( this, eng() ); + m_triangleWaveBtn->move( 136, 20 ); + m_triangleWaveBtn->setActiveGraphic( + embed::getIconPixmap( "triangle_wave_active" ) ); + m_triangleWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "triangle_wave_inactive" ) ); + toolTip::add( m_triangleWaveBtn, + tr( "Click here if you want a triangle-wave " + "for current oscillator." ) ); + connect( m_triangleWaveBtn, SIGNAL ( clicked ( void ) ), + this, SLOT ( triangleWaveClicked( void ) ) ); + + + m_sawWaveBtn = new pixmapButton( this, eng() ); + m_sawWaveBtn->move( 136, 37 ); + m_sawWaveBtn->setActiveGraphic( embed::getIconPixmap( + "saw_wave_active" ) ); + m_sawWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "saw_wave_inactive" ) ); + toolTip::add( m_sawWaveBtn, + tr( "Click here if you want a saw-wave for " + "current oscillator." ) ); + connect( m_sawWaveBtn, SIGNAL (clicked ( void ) ), + this, SLOT ( sawWaveClicked( void ) ) ); + + + m_sqrWaveBtn = new pixmapButton( this, eng() ); + m_sqrWaveBtn->move( 136, 54 ); + m_sqrWaveBtn->setActiveGraphic( embed::getIconPixmap( + "square_wave_active" ) ); + m_sqrWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "square_wave_inactive" ) ); + toolTip::add( m_sqrWaveBtn, + tr( "Click here if you want a square-wave for " + "current oscillator." ) ); + connect( m_sqrWaveBtn, SIGNAL ( clicked ( void ) ), + this, SLOT ( sqrWaveClicked( void ) ) ); + + + m_whiteNoiseWaveBtn = new pixmapButton( this, eng() ); + m_whiteNoiseWaveBtn->move( 136, 71 ); + m_whiteNoiseWaveBtn->setActiveGraphic( + embed::getIconPixmap( "white_noise_wave_active" ) ); + m_whiteNoiseWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "white_noise_wave_inactive" ) ); + toolTip::add( m_whiteNoiseWaveBtn, + tr( "Click here if you want a white-noise for " + "current oscillator." ) ); + connect( m_whiteNoiseWaveBtn, SIGNAL ( clicked ( void ) ), + this, SLOT ( noiseWaveClicked( void ) ) ); + + + m_usrWaveBtn = new pixmapButton( this, eng() ); + m_usrWaveBtn->move( 136, 88 ); + m_usrWaveBtn->setActiveGraphic( embed::getIconPixmap( + "usr_wave_active" ) ); + m_usrWaveBtn->setInactiveGraphic( embed::getIconPixmap( + "usr_wave_inactive" ) ); + toolTip::add( m_usrWaveBtn, + tr( "Click here if you want a user-defined " + "wave-shape for current oscillator." ) ); + connect( m_usrWaveBtn, SIGNAL ( clicked ( void ) ), + this, SLOT ( usrWaveClicked( void ) ) ); + + + m_smoothBtn = new pixmapButton( this, eng() ); + m_smoothBtn->move( 3, 108 ); + m_smoothBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "smooth_active" ) ); + m_smoothBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "smooth_inactive" ) ); + m_smoothBtn->setChecked( FALSE ); + toolTip::add( m_smoothBtn, + tr( "Click here to smooth waveform." ) ); + connect( m_smoothBtn, SIGNAL ( clicked ( void ) ), + this, SLOT ( smoothClicked( void ) ) ); + + + m_normalizeBtn = new pixmapButton( this, eng() ); + m_normalizeBtn->move( 20, 108 ); + m_normalizeBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( + "normalize_active" ) ); + m_normalizeBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( + "normalize_inactive" ) ); + m_normalizeBtn->setChecked( FALSE ); + toolTip::add( m_normalizeBtn, + tr( "Click here to normalize waveform." ) ); + + connect( m_normalizeBtn, SIGNAL ( clicked ( void ) ), + this, SLOT ( normalizeClicked( void ) ) ); + + m_state = new ledCheckBox( "", this, eng() ); + m_state->move( 136, 109 ); + m_state->setChecked( TRUE ); + toolTip::add( m_state, + tr( "Click here to enable/disable waveform." ) ); + + m_sampleShape = new float[m_sampleLength]; + m_graph->setSamplePointer( m_sampleShape, m_sampleLength ); + + m_lastBtn = m_sinWaveBtn; + emit( sinWaveClicked() ); + + move( _x, _y ); + +} + + + + +impulseEditor::~impulseEditor() +{ +} + + + + +void impulseEditor::sinWaveClicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_sinWaveBtn; + m_lastBtn->setChecked( TRUE ); + // generate a Sinus wave using static oscillator-method + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] = oscillator::sinSample( i / + static_cast( m_sampleLength ) ); + } + + sampleChanged(); +} + + + + +void impulseEditor::triangleWaveClicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_triangleWaveBtn; + m_lastBtn->setChecked( TRUE ); + // generate a Triangle wave using static oscillator-method + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] = oscillator::triangleSample( i / + static_cast( m_sampleLength ) ); + } + + sampleChanged(); + +} + + + + +void impulseEditor::sawWaveClicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_sawWaveBtn; + m_lastBtn->setChecked( TRUE ); + // generate a Saw wave using static oscillator-method + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] = oscillator::sawSample( i / + static_cast( m_sampleLength ) ); + } + + sampleChanged(); +} + + + + +void impulseEditor::sqrWaveClicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_sqrWaveBtn; + m_lastBtn->setChecked( TRUE ); + // generate a Sqr wave using static oscillator-method + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] = oscillator::squareSample( i / + static_cast( m_sampleLength ) ); + } + + sampleChanged(); +} + + + + +void impulseEditor::noiseWaveClicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_whiteNoiseWaveBtn; + m_lastBtn->setChecked( TRUE ); + // generate a Noise wave using static oscillator-method + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] = oscillator::noiseSample( i / + static_cast( m_sampleLength ) ); + } + + sampleChanged(); + +} + + + + +void impulseEditor::usrWaveClicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_usrWaveBtn; + m_lastBtn->setChecked( TRUE ); + // zero sample_shape + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] = 0; + } + + // load user shape + sampleBuffer buffer( eng() ); + QString af = buffer.openAudioFile(); + if( af != "" ) + { + buffer.setAudioFile( af ); + + // copy buffer data + if( m_sampleLength < static_cast( buffer.frames() ) ) + { + m_sampleLength = m_sampleLength; + } + else + { + m_sampleLength = static_cast( buffer.frames() ); + } + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] = static_cast( + buffer.data()[0][i] ); + } + } + + sampleChanged(); +} + + + + +void impulseEditor::smoothClicked( void ) +{ + m_smoothBtn->setChecked( TRUE ); + m_smoothBtn->update(); + + float* temp = new float[m_sampleLength]; + memcpy( temp, m_sampleShape, sizeof( float ) * m_sampleLength ); + + // Smoothing + m_sampleShape[0] = temp[0] / 2.0f; + for( Uint32 i = 1; i < m_sampleLength - 1; i++ ) + { + m_sampleShape[i] = ( temp[i - 1] + + temp[i] + + temp[i + 1] ) / 3.0f; + } + m_sampleShape[m_sampleLength - 1] = temp[m_sampleLength - 1] / 2.0f; + m_forward = FALSE; + + // Clean up + delete[] temp; + + // paint + update(); + m_graph->update(); + + eng()->getSongEditor()->setModified(); + + m_smoothBtn->setChecked( FALSE ); + m_smoothBtn->update(); +} + + + + +void impulseEditor::normalizeClicked( void ) +{ + m_normalizeBtn->setChecked( TRUE ); + m_normalizeBtn->update(); + + float max = 0.0001f; + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + if( fabsf(m_sampleShape[i]) > max && m_sampleShape[i] != 0.0f ) + { + max = fabs( m_sampleShape[i] ); + } + } + m_normalizeFactor = max; + + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] /= m_normalizeFactor; + } + + update(); + m_graph->update(); + + eng()->getSongEditor()->setModified(); + + m_normalizeBtn->setChecked( FALSE ); + m_normalizeBtn->update(); +} + + + + +void impulseEditor::sampleChanged() +{ + // analyze + float max = 0.0001f; + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + if( fabsf(m_sampleShape[i]) > max && m_sampleShape[i] != 0.0f ) + { + max = fabs( m_sampleShape[i] ); + } + } + m_normalizeFactor = max; + + // update + if( m_graph != NULL ) + { + m_graph->update(); + } + + eng()->getSongEditor()->setModified(); +} + + + + +void impulseEditor::setOn( bool _on ) +{ + if( _on ) + { + m_state->setChecked( TRUE ); + } + else + { + m_state->setChecked( FALSE ); + } +} + + + + +void impulseEditor::contextMenuEvent( QContextMenuEvent * ) +{ + QMenu contextMenu( this ); +#ifdef QT4 + contextMenu.setTitle( accessibleName() ); +#else + QLabel * caption = new QLabel( "" + + QString( "Impulse Editor" ) + "", this ); + caption->setPaletteBackgroundColor( QColor( 0, 0, 192 ) ); + caption->setAlignment( Qt::AlignCenter ); + contextMenu.addAction( caption ); +#endif + contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ), + this, SLOT( displayHelp() ) ); + contextMenu.exec( QCursor::pos() ); +} + + + + +void impulseEditor::displayHelp( void ) +{ +#ifdef QT4 + QWhatsThis::showText( mapToGlobal( rect().bottomRight() ), + whatsThis() ); +#else + QWhatsThis::display( QWhatsThis::textFor( this ), mapToGlobal( + rect().bottomRight() ) ); +#endif +} + + + + +void FASTCALL impulseEditor::setValues( float * _shape ) +{ + for( Uint32 i = 0; i < m_sampleLength; i++ ) + { + m_sampleShape[i] = _shape[i]; + } +} + + + + +#include "impulse_editor.moc" + diff --git a/plugins/vibed/impulse_editor.h b/plugins/vibed/impulse_editor.h new file mode 100644 index 000000000..a57de143a --- /dev/null +++ b/plugins/vibed/impulse_editor.h @@ -0,0 +1,99 @@ +/* + * impulse_editor.cpp - graphic waveform editor + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * 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 _IMPULSE_EDITOR_H +#define _IMPULSE_EDITOR_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + +#include "config.h" +#include "types.h" +#include "graph.h" +#include "pixmap_button.h" +#include "engine.h" +#include "led_checkbox.h" + +class impulseEditor: public QWidget, public engineObject +{ + Q_OBJECT +public: + impulseEditor( QWidget *parent, int _x, int _y, + engine * _engine, Uint32 _len = 128 ); + ~impulseEditor(); + + inline float * getValues() { return( m_sampleShape ); }; + inline bool isOn() { return( m_state->isChecked() ); }; + void FASTCALL setValues( float * _shape ); + +public slots: + void sinWaveClicked( void ); + void triangleWaveClicked( void ); + void sawWaveClicked( void ); + void sqrWaveClicked( void ); + void noiseWaveClicked( void ); + void usrWaveClicked( void ); + void smoothClicked( void ); + void normalizeClicked( void ); + void sampleChanged(); + void setOn( bool _on ); + void contextMenuEvent( QContextMenuEvent * ); + void displayHelp( void ); + +private: + + graph * m_graph; + pixmapButton * m_sinWaveBtn; + pixmapButton * m_triangleWaveBtn; + pixmapButton * m_sqrWaveBtn; + pixmapButton * m_sawWaveBtn; + pixmapButton * m_whiteNoiseWaveBtn; + pixmapButton * m_usrWaveBtn; + pixmapButton * m_smoothBtn; + pixmapButton * m_normalizeBtn; + pixmapButton * m_lastBtn; + ledCheckBox * m_state; + + float * m_sampleShape; + + Uint32 m_sampleLength; + float m_normalizeFactor; + bool m_forward; + + QPixmap m_base; +}; + +#endif diff --git a/plugins/vibed/logo.png b/plugins/vibed/logo.png new file mode 100644 index 000000000..ad8ab99a7 Binary files /dev/null and b/plugins/vibed/logo.png differ diff --git a/plugins/vibed/nine_button_selector.cpp b/plugins/vibed/nine_button_selector.cpp new file mode 100644 index 000000000..584f0e296 --- /dev/null +++ b/plugins/vibed/nine_button_selector.cpp @@ -0,0 +1,318 @@ +/* + * nine_button_selector.cpp + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifdef QT4 + +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +#include "nine_button_selector.h" +#include "embed.h" + +nineButtonSelector::nineButtonSelector( QPixmap _button0_on, + QPixmap _button0_off, + QPixmap _button1_on, + QPixmap _button1_off, + QPixmap _button2_on, + QPixmap _button2_off, + QPixmap _button3_on, + QPixmap _button3_off, + QPixmap _button4_on, + QPixmap _button4_off, + QPixmap _button5_on, + QPixmap _button5_off, + QPixmap _button6_on, + QPixmap _button6_off, + QPixmap _button7_on, + QPixmap _button7_off, + QPixmap _button8_on, + QPixmap _button8_off, + Uint8 _default, + Uint32 _x, Uint32 _y, + QWidget * _parent, + engine * _engine ): + QWidget( _parent, "nineButtonSelector" ), + engineObject( _engine ), + m_selected( _default ) +{ + setFixedSize( 50, 50 ); + m_base = QPixmap::grabWidget( _parent, _x, _y ); + move( _x, _y ); + setPaletteBackgroundPixmap( m_base ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 1, 1 ); + m_button->setActiveGraphic( _button0_on ); + m_button->setInactiveGraphic( _button0_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button0Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 18, 1 ); + m_button->setActiveGraphic( _button1_on ); + m_button->setInactiveGraphic( _button1_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button1Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 35, 1 ); + m_button->setActiveGraphic( _button2_on ); + m_button->setInactiveGraphic( _button2_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button2Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 1, 18 ); + m_button->setActiveGraphic( _button3_on ); + m_button->setInactiveGraphic( _button3_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button3Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 18, 18 ); + m_button->setActiveGraphic( _button4_on ); + m_button->setInactiveGraphic( _button4_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button4Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 35, 18 ); + m_button->setActiveGraphic( _button5_on ); + m_button->setInactiveGraphic( _button5_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button5Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 1, 35 ); + m_button->setActiveGraphic( _button6_on ); + m_button->setInactiveGraphic( _button6_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button6Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 18, 35 ); + m_button->setActiveGraphic( _button7_on ); + m_button->setInactiveGraphic( _button7_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button7Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_button = new pixmapButton( this, eng() ); + m_button->move( 35, 35 ); + m_button->setActiveGraphic( _button8_on ); + m_button->setInactiveGraphic( _button8_off ); + m_button->setChecked( FALSE ); + connect( m_button, SIGNAL ( clicked ( void ) ), + this, SLOT ( button8Clicked( void ) ) ); + m_buttons.append( m_button ); + + m_lastBtn = m_buttons.at( _default ); + m_lastBtn->setChecked( TRUE ); +} + + +nineButtonSelector::~ nineButtonSelector() +{ +} + + + + +void nineButtonSelector::button0Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 0 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 0; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::button1Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 1 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 1; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::button2Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 2 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 2; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::button3Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 3 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 3; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::button4Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 4 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 4; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::button5Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 5 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 5; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::button6Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 6 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 6; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::button7Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 7 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 7; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::button8Clicked( void ) +{ + m_lastBtn->setChecked( FALSE); + m_lastBtn = m_buttons.at( 8 ); + m_lastBtn->setChecked( TRUE ); + + m_selected = 8; + emit nineButtonSelection( m_selected ); +} + + + + +void nineButtonSelector::contextMenuEvent( QContextMenuEvent * ) +{ + QMenu contextMenu( this ); +#ifdef QT4 + contextMenu.setTitle( accessibleName() ); +#else + QLabel * caption = new QLabel( "" + + QString( "Selector" ) + "", this ); + caption->setPaletteBackgroundColor( QColor( 0, 0, 192 ) ); + caption->setAlignment( Qt::AlignCenter ); + contextMenu.addAction( caption ); +#endif + contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ), + this, SLOT( displayHelp() ) ); + contextMenu.exec( QCursor::pos() ); +} + + + + +void nineButtonSelector::displayHelp( void ) +{ +#ifdef QT4 + QWhatsThis::showText( mapToGlobal( rect().bottomRight() ), + whatsThis() ); +#else + QWhatsThis::display( QWhatsThis::textFor( this ), mapToGlobal( + rect().bottomRight() ) ); +#endif +} + + + + +#include "nine_button_selector.moc" diff --git a/plugins/vibed/nine_button_selector.h b/plugins/vibed/nine_button_selector.h new file mode 100644 index 000000000..153b02a90 --- /dev/null +++ b/plugins/vibed/nine_button_selector.h @@ -0,0 +1,89 @@ +/* + * nine_button_selector.h + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * 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 _NINE_BUTTON_SELECTOR_H +#define _NINE_BUTTON_SELECTOR_H + +#include + +#include "qt3support.h" +#include "config.h" +#include "types.h" +#include "pixmap_button.h" + +class nineButtonSelector: public QWidget, public engineObject +{ + Q_OBJECT + +public: + nineButtonSelector( QPixmap _button1_on, + QPixmap _button1_off, + QPixmap _button2_on, + QPixmap _button2_off, + QPixmap _button3_on, + QPixmap _button3_off, + QPixmap _button4_on, + QPixmap _button4_off, + QPixmap _button5_on, + QPixmap _button5_off, + QPixmap _button6_on, + QPixmap _button6_off, + QPixmap _button7_on, + QPixmap _button7_off, + QPixmap _button8_on, + QPixmap _button8_off, + QPixmap _button9_on, + QPixmap _button9_off, + Uint8 _default, + Uint32 _x, Uint32 _y, + QWidget * _parent, + engine * _engine ); + ~nineButtonSelector(); + + inline Uint8 getSelected() { return( m_selected ); }; + +public slots: + void button0Clicked( void ); + void button1Clicked( void ); + void button2Clicked( void ); + void button3Clicked( void ); + void button4Clicked( void ); + void button5Clicked( void ); + void button6Clicked( void ); + void button7Clicked( void ); + void button8Clicked( void ); + void contextMenuEvent( QContextMenuEvent * ); + void displayHelp( void ); + +signals: + void nineButtonSelection( Uint8 ); + +private: + QPtrList m_buttons; + pixmapButton * m_button; + pixmapButton * m_lastBtn; + QPixmap m_base; + + Uint8 m_selected; +}; +#endif diff --git a/plugins/vibed/normalize_active.png b/plugins/vibed/normalize_active.png new file mode 100644 index 000000000..bc57226a4 Binary files /dev/null and b/plugins/vibed/normalize_active.png differ diff --git a/plugins/vibed/normalize_inactive.png b/plugins/vibed/normalize_inactive.png new file mode 100644 index 000000000..3d5af0882 Binary files /dev/null and b/plugins/vibed/normalize_inactive.png differ diff --git a/plugins/vibed/smooth_active.png b/plugins/vibed/smooth_active.png new file mode 100644 index 000000000..8f0333723 Binary files /dev/null and b/plugins/vibed/smooth_active.png differ diff --git a/plugins/vibed/smooth_inactive.png b/plugins/vibed/smooth_inactive.png new file mode 100644 index 000000000..d2ad688b1 Binary files /dev/null and b/plugins/vibed/smooth_inactive.png differ diff --git a/plugins/vibed/string_container.cpp b/plugins/vibed/string_container.cpp new file mode 100644 index 000000000..c005e3e2c --- /dev/null +++ b/plugins/vibed/string_container.cpp @@ -0,0 +1,95 @@ +/* + * string_container.cpp - contains a collection of strings + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "string_container.h" + + +stringContainer::stringContainer( const float _pitch, + const sample_rate_t _sample_rate, + const Uint32 _buffer_length ) : + m_pitch( _pitch ), + m_sampleRate( _sample_rate ), + m_bufferLength( _buffer_length ) +{ +} + + + + +void stringContainer::addString(Uint8 _harm, + const float _pick, + const float _pickup, + float * _impluse, + const float _randomize, + const float _string_loss, + const float _detune, + const Uint8 _oversample, + const bool _state ) +{ + float harm; + switch( _harm ) + { + case 0: + harm = 0.25f; + break; + case 1: + harm = 0.5f; + break; + case 2: + harm = 1.0f; + break; + case 3: + harm = 2.0f; + break; + case 4: + harm = 3.0f; + break; + case 5: + harm = 4.0f; + break; + case 6: + harm = 5.0f; + break; + case 7: + harm = 6.0f; + break; + case 8: + harm = 7.0f; + break; + default: + harm = 1.0f; + } + + m_strings.append( new vibratingString( m_pitch * harm, + _pick, + _pickup, + _impluse, + m_bufferLength, + m_sampleRate, + _oversample, + _randomize, + _string_loss, + _detune, + _state ) ); +} diff --git a/plugins/vibed/string_container.h b/plugins/vibed/string_container.h new file mode 100644 index 000000000..7812c3ca5 --- /dev/null +++ b/plugins/vibed/string_container.h @@ -0,0 +1,83 @@ +/* string_container.h - contains a collection of strings + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * 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 _TWO_STRINGS_H +#define _TWO_STRINGS_H + + +#ifdef QT4 + +#include + +#else + +#include "qptrlist.h" + +#endif + + +#include "config.h" +#include "types.h" +#include "vibrating_string.h" + + + +class stringContainer +{ +public: + stringContainer( const float _pitch, + const sample_rate_t _sample_rate, + const Uint32 _buffer_length ); + + void addString( Uint8 _harm, + const float _pick, + const float _pickup, + float * _impluse, + const float _randomize, + const float _string_loss, + const float _detune, + const Uint8 _oversample, + const bool _state ); + + inline ~stringContainer() + { + m_strings.setAutoDelete( TRUE ); + Uint32 strings = m_strings.count(); + for( Uint32 i = 0; i < strings; i++ ) + { + m_strings.removeFirst(); + } + } + + inline float getStringSample( Uint8 _string ) + { + return( m_strings.at( _string )->nextSample() ); + } + +private: + QPtrList m_strings; + const float m_pitch; + const sample_rate_t m_sampleRate; + const Uint32 m_bufferLength; +} ; + +#endif diff --git a/plugins/vibed/vibed.cpp b/plugins/vibed/vibed.cpp new file mode 100644 index 000000000..397f15490 --- /dev/null +++ b/plugins/vibed/vibed.cpp @@ -0,0 +1,747 @@ +/* + * vibed.cpp - combination of PluckedStringSynth and BitInvader + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include + +#endif + + +#include "vibed.h" +#include "note_play_handle.h" +#include "instrument_track.h" +#include "templates.h" +#include "buffer_allocator.h" +#include "knob.h" +#include "tooltip.h" +#include "oscillator.h" +#include "song_editor.h" +#include "string_container.h" +#include "base64.h" + +#undef SINGLE_SOURCE_COMPILE +#include "embed.cpp" + + +extern "C" +{ + +plugin::descriptor vibedstrings_plugin_descriptor = +{ + STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), + "Vibed", + QT_TRANSLATE_NOOP( "pluginBrowser", + "Vibrating string modeler" ), + "Danny McRae ", + 0x0100, + plugin::INSTRUMENT, + new QPixmap( PLUGIN_NAME::getIconPixmap( "logo" ) ) +}; + +} + + +vibed::vibed( instrumentTrack * _channel_track ) : + instrument( _channel_track, &vibedstrings_plugin_descriptor ), + m_sampleLength( 128 ) +{ +#ifdef QT4 + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( + "artwork" ) ); + setPalette( pal ); +#else + setErasePixmap( PLUGIN_NAME::getIconPixmap( "artwork" ) ); +#endif + + for( Uint8 harm = 0; harm < 9; harm++ ) + { + m_editor = new impulseEditor( this, 76, 21, eng() ); + m_editor->setOn( FALSE ); + m_editor->hide(); + m_editors.append( m_editor ); +#ifdef QT4 + m_editor->setWhatsThis( +#else + QWhatsThis::add( m_editor, +#endif + tr( +"The waveform editor provides control over the initial state or impulse " +"that is used to start the string vibrating. The buttons to the right of " +"the graph will initialize the waveform to the selected type. The '?' " +"button will load a waveform from a file--only the first 128 samples " +"will be loaded.\n\n" + +"The waveform can also be drawn in the graph.\n\n" + +"The 'S' button will smooth the waveform.\n\n" + +"The 'N' button will normalize the waveform.") ); + + m_volumeKnob = new knob( knobBright_26, this, + tr( "Volume" ), + eng() ); + m_volumeKnob->setRange( 0.0f, 2.0f, 0.01f ); + m_volumeKnob->setInitValue( 1.0f ); + m_volumeKnob->move( 103, 142 ); + m_volumeKnob->setHintText( tr( "Volume:" ) + " ", "" ); + m_volumeKnob->hide(); + m_volumeKnobs.append( m_volumeKnob ); +#ifdef QT4 + m_volumeKnob->setWhatsThis( +#else + QWhatsThis::add( m_volumeKnob, +#endif + tr( +"The 'V' knob sets the volume of the selected string." ) ); + + m_stiffnessKnob = new knob( knobBright_26, this, + tr( "String stiffness" ), + eng() ); + m_stiffnessKnob->setRange( 0.0f, 0.05f, 0.001f ); + m_stiffnessKnob->setInitValue( 0.0f ); + m_stiffnessKnob->move( 129, 142 ); + m_stiffnessKnob->setHintText( tr( "String stiffness:" ) + + " ", "" ); + m_stiffnessKnob->hide(); + m_stiffnessKnobs.append( m_stiffnessKnob ); +#ifdef QT4 + m_stiffnessKnob->setWhatsThis( +#else + QWhatsThis::add( m_stiffnessKnob, +#endif + tr( +"The 'S' knob sets the stiffness of the selected string. The stiffness " +"of the string affects how long the string will ring out. The lower " +"the setting, the longer the string will ring." ) ); + + + m_pickKnob = new knob( knobBright_26, this, + tr( "Pick position" ), + eng() ); + m_pickKnob->setRange( 0.0f, 0.5f, 0.005f ); + m_pickKnob->setInitValue( 0.0f ); + m_pickKnob->move( 153, 142 ); + m_pickKnob->setHintText( tr( "Pick position:" ) + " ", "" ); + m_pickKnob->hide(); + m_pickKnobs.append( m_pickKnob ); +#ifdef QT4 + m_pickKnob->setWhatsThis( +#else + QWhatsThis::add( m_pickKnob, +#endif + tr( +"The 'P' knob sets the position where the selected string will be 'picked'. " +"The lower the setting the closer the pick is to the bridge." ) ); + + m_pickupKnob = new knob( knobBright_26, this, + tr( "Pickup position" ), + eng() ); + m_pickupKnob->setRange( 0.0f, 0.5f, 0.005f ); + m_pickupKnob->setInitValue( 0.05f ); + m_pickupKnob->move( 177, 142 ); + m_pickupKnob->setHintText( tr( "Pickup position:" ) + + " ", "" ); + m_pickupKnob->hide(); + m_pickupKnobs.append( m_pickupKnob ); +#ifdef QT4 + m_pickupKnob->setWhatsThis( +#else + QWhatsThis::add( m_pickupKnob, +#endif + tr( +"The 'PU' knob sets the position where the vibrations will be monitored " +"for the selected string. The lower the setting, the closer the " +"pickup is to the bridge." ) ); + + m_panKnob = new knob( knobBright_26, this, + tr( "Pan" ), + eng() ); + m_panKnob->setRange( -1.0f, 1.0f, 0.01f ); + m_panKnob->setInitValue( 0.0f ); + m_panKnob->move( 105, 187 ); + m_panKnob->setHintText( tr( "Pan:" ) + " ", "" ); + m_panKnob->hide(); + m_panKnobs.append( m_panKnob ); +#ifdef QT4 + m_panKnob->setWhatsThis( +#else + QWhatsThis::add( m_panKnob, +#endif + tr( +"The Pan knob determines the location of the selected string in the stereo " +"field." ) ); + + m_detuneKnob = new knob( knobBright_26, this, + tr( "Detune" ), + eng() ); + m_detuneKnob->setRange( -0.1f, 0.1f, 0.001f ); + m_detuneKnob->setInitValue( 0.0f ); + m_detuneKnob->move( 150, 187 ); + m_detuneKnob->setHintText( tr( "Detune:" ) + " ", "" ); + m_detuneKnob->hide(); + m_detuneKnobs.append( m_detuneKnob ); +#ifdef QT4 + m_detuneKnob->setWhatsThis( +#else + QWhatsThis::add( m_detuneKnob, +#endif + tr( +"The Detune knob modifies the pitch of the selected string. Settings less " +"than zero will cause the string to sound flat. Settings greater than zero " +"will cause the string to sound sharp." ) ); + + m_randomKnob = new knob( knobBright_26, this, + tr( "Fuzziness" ), + eng() ); + m_randomKnob->setRange( 0.0f, 0.75f, 0.01f ); + m_randomKnob->setInitValue( 0.0f ); + m_randomKnob->move( 194, 187 ); + m_randomKnob->setHintText( tr( "Fuzziness:" ) + + " ", "" ); + m_randomKnob->hide(); + m_randomKnobs.append( m_randomKnob ); +#ifdef QT4 + m_randomKnob->setWhatsThis( +#else + QWhatsThis::add( m_randomKnob, +#endif + tr( +"The Slap knob adds a bit of fuzz to the selected string which is most " +"apparent during the attack, though it can also be used to make the string " +"sound more 'metallic'.") ); + + m_lengthKnob = new knob( knobBright_26, this, + tr( "Length" ), + eng() ); + m_lengthKnob->setRange( 1, 16, 1 ); + m_lengthKnob->setInitValue( 1 ); + m_lengthKnob->move( 23, 193 ); + m_lengthKnob->setHintText( tr( "Length:" ) + + " ", "" ); + m_lengthKnob->hide(); + m_lengthKnobs.append( m_lengthKnob ); +#ifdef QT4 + m_lengthKnob->setWhatsThis( +#else + QWhatsThis::add( m_lengthKnob, +#endif + tr( +"The Length knob sets the length of the selected string. Longer strings " +"will both ring longer and sound brighter, however, they will also eat up " +"more CPU cycles." ) ); + + m_impulse = new ledCheckBox( "", this, eng() ); + m_impulse->move( 23, 94 ); + m_impulse->setChecked( FALSE ); + toolTip::add( m_impulse, + tr( "Impulse or initial state" ) ); + m_impulse->hide(); + m_impulses.append( m_impulse ); +#ifdef QT4 + m_impulse->setWhatsThis( +#else + QWhatsThis::add( m_impulse, +#endif + tr( +"The 'Imp' selector determines whether the waveform in the graph is to be " +"treated as an impulse imparted to the string by the pick or the initial " +"state of the string." ) ); + + m_harmonic = new nineButtonSelector( + PLUGIN_NAME::getIconPixmap( "button_-2_on" ), + PLUGIN_NAME::getIconPixmap( "button_-2_off" ), + PLUGIN_NAME::getIconPixmap( "button_-1_on" ), + PLUGIN_NAME::getIconPixmap( "button_-1_off" ), + PLUGIN_NAME::getIconPixmap( "button_f_on" ), + PLUGIN_NAME::getIconPixmap( "button_f_off" ), + PLUGIN_NAME::getIconPixmap( "button_2_on" ), + PLUGIN_NAME::getIconPixmap( "button_2_off" ), + PLUGIN_NAME::getIconPixmap( "button_3_on" ), + PLUGIN_NAME::getIconPixmap( "button_3_off" ), + PLUGIN_NAME::getIconPixmap( "button_4_on" ), + PLUGIN_NAME::getIconPixmap( "button_4_off" ), + PLUGIN_NAME::getIconPixmap( "button_5_on" ), + PLUGIN_NAME::getIconPixmap( "button_5_off" ), + PLUGIN_NAME::getIconPixmap( "button_6_on" ), + PLUGIN_NAME::getIconPixmap( "button_6_off" ), + PLUGIN_NAME::getIconPixmap( "button_7_on" ), + PLUGIN_NAME::getIconPixmap( "button_7_off" ), + 2, + 21, 127, + this, + eng() ); + m_harmonic->hide(); + m_harmonics.append( m_harmonic ); +#ifdef QT4 + m_harmonic->setWhatsThis( +#else + QWhatsThis::add( m_harmonic, +#endif + tr( +"The Octave selector is used to choose which harmonic of the note the " +"string will ring at. For example, '-2' means the string will ring two " +"octaves below the fundamental, 'F' means the string will ring at the " +"fundamental, and '6' means the string will ring six octaves above the " +"fundamental." ) ); + } + + m_stringSelector = new nineButtonSelector( + PLUGIN_NAME::getIconPixmap( "button_1_on" ), + PLUGIN_NAME::getIconPixmap( "button_1_off" ), + PLUGIN_NAME::getIconPixmap( "button_2_on" ), + PLUGIN_NAME::getIconPixmap( "button_2_off" ), + PLUGIN_NAME::getIconPixmap( "button_3_on" ), + PLUGIN_NAME::getIconPixmap( "button_3_off" ), + PLUGIN_NAME::getIconPixmap( "button_4_on" ), + PLUGIN_NAME::getIconPixmap( "button_4_off" ), + PLUGIN_NAME::getIconPixmap( "button_5_on" ), + PLUGIN_NAME::getIconPixmap( "button_5_off" ), + PLUGIN_NAME::getIconPixmap( "button_6_on" ), + PLUGIN_NAME::getIconPixmap( "button_6_off" ), + PLUGIN_NAME::getIconPixmap( "button_7_on" ), + PLUGIN_NAME::getIconPixmap( "button_7_off" ), + PLUGIN_NAME::getIconPixmap( "button_8_on" ), + PLUGIN_NAME::getIconPixmap( "button_8_off" ), + PLUGIN_NAME::getIconPixmap( "button_9_on" ), + PLUGIN_NAME::getIconPixmap( "button_9_off" ), + 0, + 21, 39, + this, + eng() ); + connect( m_stringSelector, SIGNAL( nineButtonSelection( Uint8 ) ), + this, SLOT( showString( Uint8 ) ) ); +#ifdef QT4 + m_stringSelector->setWhatsThis( +#else + QWhatsThis::add( m_stringSelector, +#endif + tr( +"The String selector is used to choose which string the controls are " +"editting. A Vibed instrument can contain up to nine independently " +"vibrating strings. The LED in the lower right corner of the " +"waveform editor indicates whether the selected string is active." ) ); + + m_pickKnob = m_pickKnobs.at( 0 ); + m_pickupKnob = m_pickupKnobs.at( 0 ); + m_stiffnessKnob = m_stiffnessKnobs.at( 0 ); + m_volumeKnob = m_volumeKnobs.at( 0 ); + m_panKnob = m_panKnobs.at( 0 ); + m_detuneKnob = m_detuneKnobs.at( 0 ); + m_randomKnob = m_randomKnobs.at( 0 ); + m_lengthKnob = m_lengthKnobs.at( 0 ); + m_editor = m_editors.at( 0 ); + m_impulse = m_impulses.at( 0 ); + m_harmonic = m_harmonics.at( 0 ); + + m_editor->setOn( TRUE ); + showString( 0 ); + +#ifdef QT4 + this->setWhatsThis( +#else + QWhatsThis::add( this, +#endif + tr( +"Vibed models up to nine independently vibrating strings. The 'String' " +"selector allows you to choose which string is being edited. The 'Imp' " "selector chooses whether the graph represents an impulse or the initial " +"state of the string. The 'Octave' selector chooses which harmonic the " +"string should vibrate at.\n\n" + +"The graph allows you to control the initial state or impulse used to set the " +"string in motion.\n\n" + +"The 'V' knob controls the volume. The 'S' knob controls the string's " +"stiffness. The 'P' knob controls the pick position. The 'PU' knob " +"controls the pickup position.\n\n" + +"'Pan' and 'Detune' hopefully don't need explanation. The 'Slap' knob " +"adds a bit of fuzz to the sound of the string.\n\n" + +"The 'Length' knob controls the length of the string.\n\n" + +"The LED in the lower right corner of the waveform editor determines " +"whether the string is active in the current instrument." ) ); + +} + + + + +vibed::~vibed() +{ + delete m_sampleBuffer; + m_pickKnobs.setAutoDelete( TRUE ); + m_pickupKnobs.setAutoDelete( TRUE ); + m_stiffnessKnobs.setAutoDelete( TRUE ); + m_volumeKnobs.setAutoDelete( TRUE ); + m_panKnobs.setAutoDelete( TRUE ); + m_detuneKnobs.setAutoDelete( TRUE ); + m_randomKnobs.setAutoDelete( TRUE ); + m_lengthKnobs.setAutoDelete( TRUE ); + m_editors.setAutoDelete( TRUE ); + m_impulses.setAutoDelete( TRUE ); + for( Uint8 harm = 0; harm < 9; harm++ ) + { + m_pickKnobs.removeFirst(); + m_pickupKnobs.removeFirst(); + m_stiffnessKnobs.removeFirst(); + m_volumeKnobs.removeFirst(); + m_panKnobs.removeFirst(); + m_detuneKnobs.removeFirst(); + m_randomKnobs.removeFirst(); + m_lengthKnobs.removeFirst(); + m_editors.removeFirst(); + m_impulses.removeFirst(); + m_harmonics.removeFirst(); + } +} + + + + +void vibed::saveSettings( QDomDocument & _doc, + QDomElement & _this ) +{ + QString name; + + // Save plugin version + _this.setAttribute( "version", "0.1" ); + + for( Uint8 i = 0; i < 9; i++ ) + { + name = "active" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_editors.at( i )->isOn() ) ); + if( m_editors.at( i )->isOn() ) + { + name = "volume" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_volumeKnobs.at( i )->value() ) ); + + name = "stiffness" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_stiffnessKnobs.at( i )->value() ) ); + + name = "pick" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_pickKnobs.at( i )->value() ) ); + + name = "pickup" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_pickupKnobs.at( i )->value() ) ); + + name = "length" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_lengthKnobs.at( i )->value() ) ); + + name = "pan" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_panKnobs.at( i )->value() ) ); + + name = "detune" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_detuneKnobs.at( i )->value() ) ); + + name = "slap" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_randomKnobs.at( i )->value() ) ); + + name = "impulse" + QString::number( i ); + _this.setAttribute( name, QString::number( + m_impulses.at( i )->isChecked() ) ); + + QString sampleString; + base64::encode( + (const char *)m_editors.at( i )->getValues(), + 128 * sizeof(float), sampleString ); + name = "graph" + QString::number( i ); + _this.setAttribute( name, sampleString ); + } + } + +} + + + + +void vibed::loadSettings( const QDomElement & _this ) +{ + QString name; + + for( Uint8 i = 0; i < 9; i++ ) + { + name = "active" + QString::number( i ); + m_editors.at( i )->setOn( _this.attribute( name ).toInt() ); + + if( m_editors.at( i )->isOn() ) + { + name = "volume" + QString::number( i ); + m_volumeKnobs.at( i )->setValue( + _this.attribute( name ).toFloat() ); + + name = "stiffness" + QString::number( i ); + m_stiffnessKnobs.at( i )->setValue( + _this.attribute( name ).toFloat() ); + + name = "pick" + QString::number( i ); + m_pickKnobs.at( i )->setValue( + _this.attribute( name ).toFloat() ); + + name = "pickup" + QString::number( i ); + m_pickupKnobs.at( i )->setValue( + _this.attribute( name ).toFloat() ); + + name = "length" + QString::number( i ); + m_lengthKnobs.at( i )->setValue( + _this.attribute( name ).toFloat() ); + + name = "pan" + QString::number( i ); + m_panKnobs.at( i )->setValue( + _this.attribute( name ).toFloat() ); + + name = "detune" + QString::number( i ); + m_detuneKnobs.at( i )->setValue( + _this.attribute( name ).toFloat() ); + + name = "slap" + QString::number( i ); + m_randomKnobs.at( i )->setValue( + _this.attribute( name ).toFloat() ); + + name = "impulse" + QString::number( i ); + m_impulses.at( i )->setChecked( + _this.attribute( name ).toInt() ); + + name = "graph" + QString::number( i ); + float shape[128]; + int size = 0; + QString sampleString = _this.attribute( name ); + char * dst = 0; + base64::decode( sampleString, &dst, &size ); + memcpy( shape, dst, size ); + m_editors.at( i )->setValues( shape ); + } + } + + update(); +} + + + + +QString vibed::nodeName( void ) const +{ + return( vibedstrings_plugin_descriptor.name ); +} + + + + +void vibed::playNote( notePlayHandle * _n ) +{ + if ( _n->totalFramesPlayed() == 0 ) + { + float freq = getInstrumentTrack()->frequency( _n ); + + _n->m_pluginData = new stringContainer( + freq, + eng()->getMixer()->sampleRate(), + m_sampleLength ); + + for( Uint8 i = 0; i < 9; i++ ) + { + if( m_editors.at( i )->isOn() ) + { + static_cast( + _n->m_pluginData )->addString( + m_harmonics.at( i )->getSelected(), + m_pickKnobs.at( i )->value(), + m_pickupKnobs.at( i )->value(), + m_editors.at( i )->getValues(), + m_randomKnobs.at( i )->value(), + m_stiffnessKnobs.at( i )->value(), + m_detuneKnobs.at( i )->value(), + static_cast( + m_lengthKnobs.at( i )->value() ), + m_impulses.at( i )->isChecked() ); + } + } + } + + const Uint32 frames = eng()->getMixer()->framesPerAudioBuffer(); + stringContainer * ps = static_cast( + _n->m_pluginData ); + + sampleFrame * buf = bufferAllocator::alloc( frames ); + + float vol; + float pan; + float sample; + Uint8 s; + + for( Uint32 i = 0; i < frames; i++ ) + { + buf[i][0] = 0.0f; + buf[i][1] = 0.0f; + s = 0; + for( Uint8 string = 0; string < 9; string ++ ) + { + if( m_editors.at( string )->isOn() ) + { + vol = m_volumeKnobs.at( + string )->value(); + pan = ( + m_panKnobs.at( string )->value() + 1 ) / 2.0; + sample = ps->getStringSample( s ); + + buf[i][0] += pan * vol * sample; + buf[i][1] += ( 1.0 - pan ) * vol * sample; + s++; + } + } + } + + getInstrumentTrack()->processAudioBuffer( buf, frames, _n ); + + bufferAllocator::free( buf ); +} + + + + +void vibed::deleteNotePluginData( notePlayHandle * _n ) +{ + delete static_cast( _n->m_pluginData ); +} + + + + +void vibed::showString( Uint8 _string ) +{ + m_pickKnob->hide(); + m_pickupKnob->hide(); + m_stiffnessKnob->hide(); + m_volumeKnob->hide(); + m_panKnob->hide(); + m_detuneKnob->hide(); + m_randomKnob->hide(); + m_lengthKnob->hide(); + m_editor->hide(); + m_impulse->hide(); + m_harmonic->hide(); + + m_editors.at( _string )->show(); + m_volumeKnobs.at( _string )->show(); + m_stiffnessKnobs.at( _string )->show(); + m_pickKnobs.at( _string )->show(); + m_pickupKnobs.at( _string )->show(); + m_panKnobs.at( _string )->show(); + m_detuneKnobs.at( _string )->show(); + m_randomKnobs.at( _string )->show(); + m_lengthKnobs.at( _string )->show(); + m_impulses.at( _string )->show(); + m_impulses.at( _string )->update(); + m_harmonics.at( _string )->show(); + + m_pickKnob = m_pickKnobs.at( _string ); + m_pickupKnob = m_pickupKnobs.at( _string ); + m_stiffnessKnob = m_stiffnessKnobs.at( _string ); + m_volumeKnob = m_volumeKnobs.at( _string ); + m_panKnob = m_panKnobs.at( _string ); + m_detuneKnob = m_detuneKnobs.at( _string ); + m_randomKnob = m_randomKnobs.at( _string ); + m_lengthKnob = m_lengthKnobs.at( _string ); + m_editor = m_editors.at( _string ); + m_impulse = m_impulses.at( _string ); + m_harmonic = m_harmonics.at( _string ); +} + + + + + +void vibed::contextMenuEvent( QContextMenuEvent * ) +{ + QMenu contextMenu( this ); +#ifdef QT4 + contextMenu.setTitle( accessibleName() ); +#else + QLabel * caption = new QLabel( "" + + QString( "Vibed" ) + "", this ); + caption->setPaletteBackgroundColor( QColor( 0, 0, 192 ) ); + caption->setAlignment( Qt::AlignCenter ); + contextMenu.addAction( caption ); +#endif + contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ), + this, SLOT( displayHelp() ) ); + contextMenu.exec( QCursor::pos() ); +} + + + + +void vibed::displayHelp( void ) +{ +#ifdef QT4 + QWhatsThis::showText( mapToGlobal( rect().bottomRight() ), + whatsThis() ); +#else + QWhatsThis::display( QWhatsThis::textFor( this ), mapToGlobal( + rect().bottomRight() ) ); +#endif +} + + + + +extern "C" +{ + +// neccessary for getting instance out of shared lib + plugin * lmms_plugin_main( void * _data ) + { + return( new vibed( static_cast( _data ) ) ); + } + + +} + +#include "vibed.moc" + diff --git a/plugins/vibed/vibed.h b/plugins/vibed/vibed.h new file mode 100644 index 000000000..357f0a92e --- /dev/null +++ b/plugins/vibed/vibed.h @@ -0,0 +1,100 @@ +/* vibed_strings.h - + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * 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 _VIBED_STRINGS_H +#define _VIBED_STRINGS_H + +#include + +#include "instrument.h" +#include "sample_buffer.h" +#include "graph.h" +#include "pixmap_button.h" +#include "buffer_allocator.h" +#include "led_checkbox.h" +#include "impulse_editor.h" +#include "lcd_spinbox.h" +#include "nine_button_selector.h" + +class knob; +class notePlayHandle; + + +class vibed : public instrument +{ + Q_OBJECT + +public: + vibed( instrumentTrack * _channel_track ); + virtual ~vibed(); + + virtual void FASTCALL playNote( notePlayHandle * _n ); + virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + virtual QString nodeName( void ) const; + +public slots: + void showString( Uint8 _string ); + void contextMenuEvent( QContextMenuEvent * ); + void displayHelp( void ); + +private: + QPtrList m_pickKnobs; + QPtrList m_pickupKnobs; + QPtrList m_stiffnessKnobs; + QPtrList m_volumeKnobs; + QPtrList m_panKnobs; + QPtrList m_detuneKnobs; + QPtrList m_randomKnobs; + QPtrList m_lengthKnobs; + QPtrList m_editors; + QPtrList m_impulses; + QPtrList m_harmonics; + + knob * m_pickKnob; + knob * m_pickupKnob; + knob * m_stiffnessKnob; + knob * m_volumeKnob; + knob * m_panKnob; + knob * m_detuneKnob; + knob * m_randomKnob; + knob * m_lengthKnob; + impulseEditor * m_editor; + + nineButtonSelector * m_stringSelector; + nineButtonSelector * m_harmonic; + + ledCheckBox * m_impulse; + + sampleBuffer * m_sampleBuffer; + + int m_sampleLength; + +} ; + + +#endif diff --git a/plugins/vibed/vibrating_string.cpp b/plugins/vibed/vibrating_string.cpp new file mode 100644 index 000000000..c68d27483 --- /dev/null +++ b/plugins/vibed/vibrating_string.cpp @@ -0,0 +1,158 @@ +/* + * vibrating_sring.h - model of a vibrating string lifted from pluckedSynth + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * 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 + +#include "vibrating_string.h" +#include "templates.h" +#include "interpolation.h" + + +vibratingString::vibratingString( float _pitch, + float _pick, + float _pickup, + float * _impulse, + Uint32 _len, + sample_rate_t _sample_rate, + Uint8 _oversample, + float _randomize, + float _string_loss, + float _detune, + bool _state ): + m_oversample( _oversample ), + m_randomize( _randomize ), + m_stringLoss( 1.0f - _string_loss ), + m_state( 0.1f ) +{ + int string_length; + + string_length = static_cast( m_oversample *_sample_rate / + _pitch ) + 1; + string_length += static_cast( string_length * -_detune ); + + int pick = static_cast( ceil( string_length * _pick ) ); + + if( not _state ) + { + m_impulse = bufferAllocator::alloc( string_length ); + resample( _impulse, _len, string_length ); + } + else + { + m_impulse = bufferAllocator::alloc( _len ); + for( Uint32 i = 0; i < _len; i++ ) + { + m_impulse[i] = _impulse[i]; + } + } + + m_toBridge = vibratingString::initDelayLine( string_length, pick ); + m_fromBridge = vibratingString::initDelayLine( string_length, pick ); + + + vibratingString::setDelayLine( m_toBridge, pick, + m_impulse, _len, 0.5f, + _state ); + vibratingString::setDelayLine( m_fromBridge, pick, + m_impulse, _len, 0.5f, + _state); + + m_choice = static_cast( m_oversample * + static_cast( rand() ) / RAND_MAX ); + + m_pickupLoc = static_cast( _pickup * string_length ); +} + + + + +vibratingString::delayLine * FASTCALL vibratingString::initDelayLine( + int _len, + int _pick ) +{ + delayLine * dl = new vibratingString::delayLine[_len]; + dl->length = _len; + if( _len > 0 ) + { + dl->data = new sample_t[_len]; + float r; + float offset = 0.0f; + for( int i = 0; i < dl->length; i++ ) + { + r = static_cast( rand() ) / + RAND_MAX; + offset = ( m_randomize / 2.0f - + m_randomize ) * r; + dl->data[i] = offset; + } + } + else + { + dl->data = NULL; + } + + dl->pointer = dl->data; + dl->end = dl->data + _len - 1; + + return( dl ); +} + + + + +void FASTCALL vibratingString::freeDelayLine( delayLine * _dl ) +{ + if( _dl && _dl->data ) + { + delete[] _dl->data; + } + + _dl->data = NULL; + delete[] _dl; +} + + + + +void FASTCALL vibratingString::resample( float *_src, + f_cnt_t _src_frames, + f_cnt_t _dst_frames ) +{ + for( f_cnt_t frame = 0; frame < _dst_frames; ++frame ) + { + const float src_frame_float = frame * + (float) _src_frames / + _dst_frames; + const float frac_pos = src_frame_float - + static_cast( src_frame_float ); + const f_cnt_t src_frame = tLimit( + static_cast( src_frame_float ), + 1, _src_frames - 3 ); + m_impulse[frame] = cubicInterpolate( + _src[src_frame - 1], + _src[src_frame + 0], + _src[src_frame + 1], + _src[src_frame + 2], + frac_pos ); + } +} diff --git a/plugins/vibed/vibrating_string.h b/plugins/vibed/vibrating_string.h new file mode 100644 index 000000000..74e974a91 --- /dev/null +++ b/plugins/vibed/vibrating_string.h @@ -0,0 +1,272 @@ +/* + * vibrating_string.h - model of a vibrating string lifted from pluckedSynth + * + * Copyright (c) 2006 Danny McRae + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * 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 _VIBRATING_STRING_H +#define _VIBRATING_STRING_H + +#include +#include + +#include "config.h" +#include "types.h" +#include "buffer_allocator.h" + +class vibratingString +{ +public: + vibratingString( float _pitch, + float _pick, + float _pickup, + float * impluse, + Uint32 _len, + sample_rate_t _sample_rate, + Uint8 _oversample, + float _randomize, + float _string_loss, + float _detune, + bool _state ); + + inline ~vibratingString( void ) + { + bufferAllocator::free( m_impulse ); + vibratingString::freeDelayLine( m_fromBridge ); + vibratingString::freeDelayLine( m_toBridge ); + } + + inline sample_t nextSample( void ) + { + sample_t outsamp[m_oversample]; + sample_t ym0; + sample_t ypM; + for( Uint8 i = 0; i < m_oversample; i++) + { + // Output at pickup position + outsamp[i] = fromBridgeAccess( m_fromBridge, + m_pickupLoc ); + outsamp[i] += toBridgeAccess( m_toBridge, + m_pickupLoc ); + + // Sample traveling into "bridge" + ym0 = toBridgeAccess( m_toBridge, 1 ); + // Sample to "nut" + ypM = fromBridgeAccess( m_fromBridge, + m_fromBridge->length - 2 ); + + // String state update + + // Decrement pointer and then update + fromBridgeUpdate( m_fromBridge, + -bridgeReflection( ym0 ) ); + // Update and then increment pointer + toBridgeUpdate( m_toBridge, -ypM ); + } + return( outsamp[m_choice] ); + } + +private: + struct delayLine + { + sample_t * data; + int length; + sample_t * pointer; + sample_t * end; + } ; + + delayLine * m_fromBridge; + delayLine * m_toBridge; + int m_pickupLoc; + Uint8 m_oversample; + float m_randomize; + float m_stringLoss; + + float * m_impulse; + int m_choice; + float m_state; + + delayLine * FASTCALL initDelayLine( int _len, int _pick ); + static void FASTCALL freeDelayLine( delayLine * _dl ); + void FASTCALL resample( float *_src, + sample_rate_t _src_frames, + sample_rate_t _dst_frames ); + + /* setDelayLine initializes the string with an impulse at the pick + * position unless the impulse is longer than the string, in which + * case the impulse gets truncated. */ + inline void setDelayLine( delayLine * _dl, + int _pick, + const float * _values, + int _len, + float _scale, + bool _state ) + { + float r; + float offset; + + if( not _state ) + { + for( int i = 0; i < _pick; i++ ) + { + r = static_cast( rand() ) / + RAND_MAX; + offset = ( m_randomize / 2.0f - + m_randomize ) * r; + _dl->data[i] = _scale * + _values[_dl->length - i] + + offset; + } + for( int i = _pick; i < _dl->length; i++ ) + { + r = static_cast( rand() ) / + RAND_MAX; + offset = ( m_randomize / 2.0f - + m_randomize ) * r; + _dl->data[i] = _scale * + _values[i - _pick] + offset ; + } + } + else + { + if( _len + _pick > _dl->length ) + { + for( int i = _pick; i < _dl->length; i++ ) + { + r = static_cast( rand() ) / + RAND_MAX; + offset = ( m_randomize / 2.0f - + m_randomize ) * r; + _dl->data[i] = _scale * + _values[i-_pick] + + offset; + } + } + else + { + for( int i = 0; i < _len; i++ ) + { + r = static_cast( rand() ) / + RAND_MAX; + offset = ( m_randomize / 2.0f - + m_randomize ) * r; + _dl->data[i+_pick] = _scale * + _values[i] + + offset; + } + } + } + } + + /* toBridgeUpdate(dl, insamp); + * Places "nut-reflected" sample from upper delay-line into + * current lower delay-line pointer position (which represents + * x = 0 position). The pointer is then incremented (i.e. the + * wave travels one sample to the left), turning the previous + * position into an "effective" x = L position for the next + * iteration. */ + inline void toBridgeUpdate( delayLine * _dl, sample_t _insamp ) + { + register sample_t * ptr = _dl->pointer; + *ptr = _insamp * m_stringLoss; + ++ptr; + if( ptr > _dl->end ) + { + ptr = _dl->data; + } + _dl->pointer = ptr; + } + + /* fromBridgeUpdate(dl, insamp); + * Decrements current upper delay-line pointer position (i.e. + * the wave travels one sample to the right), moving it to the + * "effective" x = 0 position for the next iteration. The + * "bridge-reflected" sample from lower delay-line is then placed + * into this position. */ + inline void fromBridgeUpdate( delayLine * _dl, + sample_t _insamp ) + { + register sample_t * ptr = _dl->pointer; + --ptr; + if( ptr < _dl->data ) + { + ptr = _dl->end; + } + *ptr = _insamp * m_stringLoss; + _dl->pointer = ptr; + } + + /* dlAccess(dl, position); + * Returns sample "position" samples into delay-line's past. + * Position "0" points to the most recently inserted sample. */ + static inline sample_t dlAccess( delayLine * _dl, int _position ) + { + sample_t * outpos = _dl->pointer + _position; + while( outpos < _dl->data ) + { + outpos += _dl->length; + } + while( outpos > _dl->end ) + { + outpos -= _dl->length; + } + return( *outpos ); + } + + /* + * Right-going delay line: + * -->---->---->--- + * x=0 + * (pointer) + * Left-going delay line: + * --<----<----<--- + * x=0 + * (pointer) + */ + + /* fromBridgeAccess(dl, position); + * Returns spatial sample at position "position", where position zero + * is equal to the current upper delay-line pointer position (x = 0). + * In a right-going delay-line, position increases to the right, and + * delay increases to the right => left = past and right = future. */ + static inline sample_t fromBridgeAccess( delayLine * _dl, + int _position ) + { + return( dlAccess( _dl, _position ) ); + } + + /* toBridgeAccess(dl, position); + * Returns spatial sample at position "position", where position zero + * is equal to the current lower delay-line pointer position (x = 0). + * In a left-going delay-line, position increases to the right, and + * delay DEcreases to the right => left = future and right = past. */ + static inline sample_t toBridgeAccess( delayLine * _dl, int _position ) + { + return( dlAccess( _dl, _position ) ); + } + + inline sample_t bridgeReflection( sample_t _insamp ) + { + return( m_state = ( m_state + _insamp ) * 0.5 ); + } + +} ; + +#endif diff --git a/plugins/vibed/wavegraph4.png b/plugins/vibed/wavegraph4.png new file mode 100644 index 000000000..07dbd91a9 Binary files /dev/null and b/plugins/vibed/wavegraph4.png differ diff --git a/src/core/arp_and_chords_tab_widget.cpp b/src/core/arp_and_chords_tab_widget.cpp index 2a30cb007..9864c5ce5 100644 --- a/src/core/arp_and_chords_tab_widget.cpp +++ b/src/core/arp_and_chords_tab_widget.cpp @@ -472,9 +472,10 @@ void arpAndChordsTabWidget::processNote( notePlayHandle * _n ) // now follows code for arpeggio - if( _n->baseNote() == FALSE || m_arpDirectionBtnGrp->value() == OFF || - !m_arpGroupBox->isActive() || - ( _n->released() && _n->releaseFramesDone() >= + if( _n->baseNote() == FALSE || + ( m_arpDirectionBtnGrp->value() + 1) == OFF || + !m_arpGroupBox->isActive() || + ( _n->released() && _n->releaseFramesDone() >= _n->actualReleaseFramesToDo() ) ) { return; @@ -483,13 +484,13 @@ void arpAndChordsTabWidget::processNote( notePlayHandle * _n ) const int selected_arp = m_arpComboBox->value(); - constNotePlayHandleVector cnphv = notePlayHandle::nphsOfChannelTrack( - _n->getInstrumentTrack() ); + constNotePlayHandleVector cnphv = notePlayHandle::nphsOfInstrumentTrack( + _n->getInstrumentTrack() ); if( m_arpModeComboBox->value() != FREE && cnphv.size() == 0 ) { // maybe we're playing only a preset-preview-note? - cnphv = presetPreviewPlayHandle::nphsOfChannelTrack( - _n->getInstrumentTrack() ); + cnphv = presetPreviewPlayHandle::nphsOfInstrumentTrack( + _n->getInstrumentTrack() ); if( cnphv.size() == 0 ) { // still nothing found here, so lets return @@ -654,7 +655,7 @@ void arpAndChordsTabWidget::saveSettings( QDomDocument & _doc, _this.setAttribute( "arprange", m_arpRangeKnob->value() ); _this.setAttribute( "arptime", m_arpTimeKnob->value() ); _this.setAttribute( "arpgate", m_arpGateKnob->value() ); - _this.setAttribute( "arpdir", m_arpDirectionBtnGrp->value() ); + _this.setAttribute( "arpdir", m_arpDirectionBtnGrp->value() + 1 ); _this.setAttribute( "arpsyncmode", ( int ) m_arpTimeKnob->getSyncMode() ); @@ -675,14 +676,14 @@ void arpAndChordsTabWidget::loadSettings( const QDomElement & _this ) m_arpTimeKnob->setValue( _this.attribute( "arptime" ).toFloat() ); m_arpGateKnob->setValue( _this.attribute( "arpgate" ).toFloat() ); m_arpDirectionBtnGrp->setInitValue( - _this.attribute( "arpdir" ).toInt() ); + _this.attribute( "arpdir" ).toInt() - 1 ); m_arpTimeKnob->setSyncMode( ( tempoSyncKnob::tempoSyncMode ) _this.attribute( "arpsyncmode" ).toInt() ); m_arpModeComboBox->setValue( _this.attribute( "arpmode" ).toInt() ); - m_arpGroupBox->setState( m_arpDirectionBtnGrp->value() != OFF && + m_arpGroupBox->setState( _this.attribute( "arpdir" ).toInt() != OFF && !_this.attribute( "arpdisabled" ).toInt() ); } diff --git a/src/core/import_filter.cpp b/src/core/import_filter.cpp index e72bab62b..ddf3c3e7a 100644 --- a/src/core/import_filter.cpp +++ b/src/core/import_filter.cpp @@ -27,6 +27,7 @@ #include "import_filter.h" #include "track_container.h" +#include "project_journal.h" #ifdef QT4 @@ -75,6 +76,10 @@ void importFilter::import( const QString & _file_to_import, #endif ); + // do not record changes while importing files + const bool j = _tc->eng()->getProjectJournal()->isJournalling(); + _tc->eng()->getProjectJournal()->setJournalling( FALSE ); + for( vvector::iterator it = d.begin(); it != d.end(); ++it ) { @@ -93,6 +98,8 @@ void importFilter::import( const QString & _file_to_import, } } + _tc->eng()->getProjectJournal()->setJournalling( j ); + delete[] s; if( successful == FALSE ) diff --git a/src/core/note_play_handle.cpp b/src/core/note_play_handle.cpp index 8a2ad2c8e..11ccdbbf3 100644 --- a/src/core/note_play_handle.cpp +++ b/src/core/note_play_handle.cpp @@ -348,7 +348,7 @@ int notePlayHandle::index( void ) const -constNotePlayHandleVector notePlayHandle::nphsOfChannelTrack( +constNotePlayHandleVector notePlayHandle::nphsOfInstrumentTrack( const instrumentTrack * _it ) { const playHandleVector & phv = _it->eng()->getMixer()->playHandles(); diff --git a/src/core/preset_preview_play_handle.cpp b/src/core/preset_preview_play_handle.cpp index 10a9c67cc..178fa725e 100644 --- a/src/core/preset_preview_play_handle.cpp +++ b/src/core/preset_preview_play_handle.cpp @@ -56,12 +56,15 @@ class previewTrackContainer : public trackContainer public: previewTrackContainer( engine * _engine ) : trackContainer( _engine ), - m_previewChannelTrack( dynamic_cast( - track::create( track::CHANNEL_TRACK, - this ) )), + m_previewInstrumentTrack( NULL ), m_previewNote( NULL ), m_dataMutex() { + setJournalling( FALSE ); + m_previewInstrumentTrack = dynamic_cast( + track::create( track::CHANNEL_TRACK, + this ) ); + m_previewInstrumentTrack->setJournalling( FALSE ); hide(); } @@ -81,9 +84,9 @@ public: return( "previewtc" ); } - instrumentTrack * previewChannelTrack( void ) + instrumentTrack * previewInstrumentTrack( void ) { - return( m_previewChannelTrack ); + return( m_previewInstrumentTrack ); } notePlayHandle * previewNote( void ) @@ -108,7 +111,7 @@ public: private: - instrumentTrack * m_previewChannelTrack; + instrumentTrack * m_previewInstrumentTrack; notePlayHandle * m_previewNote; QMutex m_dataMutex; @@ -141,21 +144,21 @@ presetPreviewPlayHandle::presetPreviewPlayHandle( multimediaProject mmp( _preset_file ); - previewTC()->previewChannelTrack()->loadTrackSpecificSettings( + printf("load track sp\n"); + previewTC()->previewInstrumentTrack()->loadTrackSpecificSettings( mmp.content().firstChild().toElement() ); + printf("here\n"); // make sure, our preset-preview-track does not appear in any MIDI- // devices list, so just disable receiving/sending MIDI-events at all - previewTC()->previewChannelTrack()->m_midiPort->setMode( + previewTC()->previewInstrumentTrack()->m_midiPort->setMode( midiPort::DUMMY ); - // create temporary note - note n(); // create note-play-handle for it - m_previewNote = new notePlayHandle( previewTC()->previewChannelTrack(), - 0, ~0, + m_previewNote = new notePlayHandle( + previewTC()->previewInstrumentTrack(), 0, ~0, note( NULL, 0, 0, static_cast( A ), - static_cast( DEFAULT_OCTAVE-1 ), 100 ) ); + static_cast( DEFAULT_OCTAVE - 1 ), 100 ) ); previewTC()->setPreviewNote( m_previewNote ); @@ -210,7 +213,7 @@ void presetPreviewPlayHandle::cleanUp( engine * _engine ) -constNotePlayHandleVector presetPreviewPlayHandle::nphsOfChannelTrack( +constNotePlayHandleVector presetPreviewPlayHandle::nphsOfInstrumentTrack( const instrumentTrack * _it ) { constNotePlayHandleVector cnphv; diff --git a/src/core/song_editor.cpp b/src/core/song_editor.cpp index 537a661f1..71f34729a 100644 --- a/src/core/song_editor.cpp +++ b/src/core/song_editor.cpp @@ -1368,7 +1368,8 @@ void songEditor::clearProject( void ) eng()->getProjectNotes()->clear(); - eng()->getProjectJournal()->clear(); + eng()->getProjectJournal()->clearInvalidJournallingObjects(); + eng()->getProjectJournal()->clearJournal(); eng()->getProjectJournal()->setJournalling( TRUE ); } diff --git a/src/core/track.cpp b/src/core/track.cpp index adba617d4..9db270d75 100644 --- a/src/core/track.cpp +++ b/src/core/track.cpp @@ -595,7 +595,7 @@ trackContentWidget::~trackContentWidget() -trackContentObject * FASTCALL trackContentWidget::getTCO( csize _tco_num ) +trackContentObject * trackContentWidget::getTCO( csize _tco_num ) { if( _tco_num < m_trackContentObjects.size() ) { @@ -618,8 +618,7 @@ csize trackContentWidget::numOfTCOs( void ) -trackContentObject * FASTCALL trackContentWidget::addTCO( - trackContentObject * _tco ) +trackContentObject * trackContentWidget::addTCO( trackContentObject * _tco ) { QMap map; map["id"] = _tco->id(); @@ -639,7 +638,7 @@ trackContentObject * FASTCALL trackContentWidget::addTCO( -void FASTCALL trackContentWidget::removeTCO( csize _tco_num, bool _also_delete ) +void trackContentWidget::removeTCO( csize _tco_num, bool _also_delete ) { removeTCO( getTCO( _tco_num ), _also_delete ); } @@ -661,10 +660,12 @@ void trackContentWidget::removeTCO( trackContentObject * _tco, map["id"] = _tco->id(); map["state"] = mmp.toString(); addJournalEntry( journalEntry( REMOVE_TCO, map ) ); + if( _also_delete ) { delete _tco; } + m_trackContentObjects.erase( it ); getTrack()->eng()->getSongEditor()->setModified(); } @@ -873,15 +874,16 @@ void trackContentWidget::undoStep( journalEntry & _je ) case ADD_TCO: { QMap map = _je.data().toMap(); - journallingObject * jo = - eng()->getProjectJournal()->getJournallingObject( map["id"].toInt() ); - assert( jo != NULL ); + trackContentObject * tco = +dynamic_cast( + eng()->getProjectJournal()->getJournallingObject( map["id"].toInt() ) ); + assert( tco != NULL ); multimediaProject mmp( multimediaProject::JOURNAL_DATA ); - jo->saveState( mmp, mmp.content() ); + tco->saveState( mmp, mmp.content() ); map["state"] = mmp.toString(); _je.data() = map; - delete jo; + tco->close(); break; } @@ -1141,6 +1143,7 @@ void trackOperationsWidget::muteBtnRightClicked( void ) trackWidget::trackWidget( track * _track, QWidget * _parent ) : QWidget( _parent ), + journallingObject( _track->eng() ), m_track( _track ), m_trackOperationsWidget( this ), m_trackSettingsWidget( this ), @@ -1232,6 +1235,46 @@ void trackWidget::changePosition( const midiTime & _new_pos ) +void trackWidget::undoStep( journalEntry & _je ) +{ + saveJournallingState( FALSE ); + switch( _je.actionID() ) + { + case MOVE_TRACK: + { + trackContainer * tc = m_track->getTrackContainer(); + if( _je.data().toInt() > 0 ) + { + tc->moveTrackUp( m_track ); + } + else + { + tc->moveTrackDown( m_track ); + } + break; + } + case RESIZE_TRACK: + setFixedHeight( tMax( height() + + _je.data().toInt(), + MINIMAL_TRACK_HEIGHT ) ); + m_track->getTrackContainer()->realignTracks(); + break; + } + restoreJournallingState(); +} + + + + +void trackWidget::redoStep( journalEntry & _je ) +{ + journalEntry je( _je.actionID(), -_je.data().toInt() ); + undoStep( je ); +} + + + + void trackWidget::dragEnterEvent( QDragEnterEvent * _dee ) { stringPairDrag::processDragEnterEvent( _dee, "track_" + @@ -1320,6 +1363,7 @@ void trackWidget::mouseMoveEvent( QMouseEvent * _me ) { tc->moveTrackDown( m_track ); } + addJournalEntry( journalEntry( MOVE_TRACK, _me->y() ) ); } } else if( m_action == RESIZE_TRACK ) @@ -1429,7 +1473,7 @@ track::~track() -track * FASTCALL track::create( trackTypes _tt, trackContainer * _tc ) +track * track::create( trackTypes _tt, trackContainer * _tc ) { // while adding track, pause mixer for not getting into any trouble // because of track being not created completely so far @@ -1460,8 +1504,7 @@ track * FASTCALL track::create( trackTypes _tt, trackContainer * _tc ) -track * FASTCALL track::create( const QDomElement & _this, - trackContainer * _tc ) +track * track::create( const QDomElement & _this, trackContainer * _tc ) { track * t = create( static_cast( _this.attribute( "type" ).toInt() ), _tc ); @@ -1472,7 +1515,7 @@ track * FASTCALL track::create( const QDomElement & _this, -track * FASTCALL track::clone( track * _track ) +track * track::clone( track * _track ) { QDomDocument doc; QDomElement parent = doc.createElement( "clone" ); @@ -1492,7 +1535,7 @@ tact track::length( void ) const -void FASTCALL track::saveSettings( QDomDocument & _doc, QDomElement & _this ) +void track::saveSettings( QDomDocument & _doc, QDomElement & _this ) { csize num_of_tcos = getTrackContentWidget()->numOfTCOs(); @@ -1518,7 +1561,7 @@ void FASTCALL track::saveSettings( QDomDocument & _doc, QDomElement & _this ) -void FASTCALL track::loadSettings( const QDomElement & _this ) +void track::loadSettings( const QDomElement & _this ) { if( _this.attribute( "type" ).toInt() != type() ) { @@ -1550,7 +1593,11 @@ void FASTCALL track::loadSettings( const QDomElement & _this ) trackContentObject * tco = createTCO( midiTime( 0 ) ); tco->restoreState( node.toElement() ); + getTrackContentWidget()->saveJournallingState( + FALSE ); addTCO( tco ); + getTrackContentWidget()-> + restoreJournallingState(); } } node = node.nextSibling(); @@ -1566,7 +1613,7 @@ void FASTCALL track::loadSettings( const QDomElement & _this ) -trackContentObject * FASTCALL track::addTCO( trackContentObject * _tco ) +trackContentObject * track::addTCO( trackContentObject * _tco ) { return( getTrackContentWidget()->addTCO( _tco ) ); } @@ -1574,7 +1621,7 @@ trackContentObject * FASTCALL track::addTCO( trackContentObject * _tco ) -void FASTCALL track::removeTCO( csize _tco_num ) +void track::removeTCO( csize _tco_num ) { getTrackContentWidget()->removeTCO( _tco_num ); } @@ -1590,7 +1637,7 @@ csize track::numOfTCOs( void ) -trackContentObject * FASTCALL track::getTCO( csize _tco_num ) +trackContentObject * track::getTCO( csize _tco_num ) { return( getTrackContentWidget()->getTCO( _tco_num ) ); @@ -1599,7 +1646,7 @@ trackContentObject * FASTCALL track::getTCO( csize _tco_num ) -csize FASTCALL track::getTCONum( trackContentObject * _tco ) +csize track::getTCONum( trackContentObject * _tco ) { for( csize i = 0; i < getTrackContentWidget()->numOfTCOs(); ++i ) { @@ -1608,16 +1655,14 @@ csize FASTCALL track::getTCONum( trackContentObject * _tco ) return( i ); } } -#ifdef LMMS_DEBUG - qFatal( "track::getTCONum(...) -> _tco not found!\n" ); -#endif + qWarning( "track::getTCONum(...) -> _tco not found!\n" ); return( 0 ); } -void FASTCALL track::getTCOsInRange( vlist & _tco_v, +void track::getTCOsInRange( vlist & _tco_v, const midiTime & _start, const midiTime & _end ) { @@ -1655,7 +1700,7 @@ void FASTCALL track::getTCOsInRange( vlist & _tco_v, -void FASTCALL track::swapPositionOfTCOs( csize _tco_num1, csize _tco_num2 ) +void track::swapPositionOfTCOs( csize _tco_num1, csize _tco_num2 ) { getTrackContentWidget()->swapPositionOfTCOs( _tco_num1, _tco_num2 ); } diff --git a/src/core/track_container.cpp b/src/core/track_container.cpp index 2e05058a9..543d8cdb0 100644 --- a/src/core/track_container.cpp +++ b/src/core/track_container.cpp @@ -61,6 +61,7 @@ #include "import_filter.h" #include "instrument.h" #include "rubberband.h" +#include "project_journal.h" @@ -202,6 +203,10 @@ void trackContainer::cloneTrack( track * _track ) void trackContainer::addTrack( track * _track ) { + QMap map; + map["id"] = _track->id(); + addJournalEntry( journalEntry( ADD_TRACK, map ) ); + m_trackWidgets.push_back( _track->getTrackWidget() ); #ifndef QT4 m_scrollArea->addChild( _track->getTrackWidget() ); @@ -221,6 +226,13 @@ void trackContainer::removeTrack( track * _track ) m_trackWidgets.end(), _track->getTrackWidget() ); if( it != m_trackWidgets.end() ) { + QMap map; + multimediaProject mmp( multimediaProject::JOURNAL_DATA ); + _track->saveState( mmp, mmp.content() ); + map["id"] = _track->id(); + map["state"] = mmp.toString(); + addJournalEntry( journalEntry( REMOVE_TRACK, map ) ); + eng()->getMixer()->pause(); #ifndef QT4 m_scrollArea->removeChild( _track->getTrackWidget() ); @@ -417,6 +429,60 @@ void trackContainer::setPixelsPerTact( Uint16 _ppt ) +void trackContainer::undoStep( journalEntry & _je ) +{ + saveJournallingState( FALSE ); + switch( _je.actionID() ) + { + case ADD_TRACK: + { + QMap map = _je.data().toMap(); + track * tr = +dynamic_cast( + eng()->getProjectJournal()->getJournallingObject( map["id"].toInt() ) ); + assert( tr != NULL ); + multimediaProject mmp( + multimediaProject::JOURNAL_DATA ); + tr->saveState( mmp, mmp.content() ); + map["state"] = mmp.toString(); + _je.data() = map; + removeTrack( tr ); + break; + } + + case REMOVE_TRACK: + { + multimediaProject mmp( + _je.data().toMap()["state"].toString(), FALSE ); + track::create( mmp.content().firstChild().toElement(), + this ); + break; + } + } + restoreJournallingState(); +} + + + + +void trackContainer::redoStep( journalEntry & _je ) +{ + switch( _je.actionID() ) + { + case ADD_TRACK: + case REMOVE_TRACK: + _je.actionID() = ( _je.actionID() == ADD_TRACK ) ? + REMOVE_TRACK : ADD_TRACK; + undoStep( _je ); + _je.actionID() = ( _je.actionID() == ADD_TRACK ) ? + REMOVE_TRACK : ADD_TRACK; + break; + } +} + + + + void trackContainer::dragEnterEvent( QDragEnterEvent * _dee ) { stringPairDrag::processDragEnterEvent( _dee, diff --git a/src/lib/journalling_object.cpp b/src/lib/journalling_object.cpp index da0e667a1..37944bd60 100644 --- a/src/lib/journalling_object.cpp +++ b/src/lib/journalling_object.cpp @@ -69,6 +69,7 @@ journallingObject::~journallingObject() void journallingObject::undo( void ) { + printf("undo: %d\n", id() ); if( m_journalEntries.empty() == TRUE ) { return; @@ -85,6 +86,7 @@ void journallingObject::undo( void ) void journallingObject::redo( void ) { + printf("undo: %d\n", id() ); if( m_journalEntries.empty() == TRUE ) { return; @@ -103,9 +105,9 @@ QDomElement journallingObject::saveState( QDomDocument & _doc, QDomElement & _parent ) { QDomElement _this = _doc.createElement( nodeName() ); + _parent.appendChild( _this ); saveSettings( _doc, _this ); saveJournal( _doc, _this ); - _parent.appendChild( _this ); return( _this ); } @@ -115,7 +117,11 @@ QDomElement journallingObject::saveState( QDomDocument & _doc, void journallingObject::restoreState( const QDomElement & _this ) { saveJournallingState( FALSE ); + + // load actual settings loadSettings( _this ); + + // search for journal-node QDomNode node = _this.firstChild(); while( !node.isNull() ) { @@ -125,13 +131,14 @@ void journallingObject::restoreState( const QDomElement & _this ) } node = node.nextSibling(); } + restoreJournallingState(); } -void journallingObject::addJournalEntry( const journalEntry & _edit_step ) +void journallingObject::addJournalEntry( const journalEntry & _je ) { if( !( eng() == NULL || eng()->getProjectJournal()->isJournalling() == FALSE || @@ -139,7 +146,7 @@ void journallingObject::addJournalEntry( const journalEntry & _edit_step ) { m_journalEntries.erase( m_currentJournalEntry, m_journalEntries.end() ); - m_journalEntries.push_back( _edit_step ); + m_journalEntries.push_back( _je ); m_currentJournalEntry = m_journalEntries.end(); eng()->getProjectJournal()->journalEntryAdded( id() ); } diff --git a/src/lib/mmp.cpp b/src/lib/mmp.cpp index 5f00332ad..e898e765f 100644 --- a/src/lib/mmp.cpp +++ b/src/lib/mmp.cpp @@ -152,10 +152,8 @@ multimediaProject::multimediaProject( const QString & _in_file_name, } } - QDomElement root = documentElement(); m_type = type( root.attribute( "type" ) ); - QDomNode node = root.firstChild(); while( !node.isNull() ) { @@ -308,7 +306,10 @@ QString multimediaProject::typeName( projectTypes _project_type ) { if( _project_type >= UNKNOWN && _project_type < PROJ_TYPE_COUNT ) { - return( s_types[_project_type].m_name ); + return( s_types[_project_type].m_name +#warning compat-code, remove in 0.3.0 + .section( ',', 0, 0 ) + ); } return( s_types[UNKNOWN].m_name ); } diff --git a/src/lib/project_journal.cpp b/src/lib/project_journal.cpp index 6bed987bf..b1381f904 100644 --- a/src/lib/project_journal.cpp +++ b/src/lib/project_journal.cpp @@ -80,6 +80,7 @@ void projectJournal::redo( void ) journallingObject * jo; + printf("%d\n", m_joIDs[*(m_currentJournalEntry+1)] ); if( m_currentJournalEntry < m_journalEntries.end() && ( jo = m_joIDs[*m_currentJournalEntry++] ) != NULL ) { @@ -97,7 +98,7 @@ void projectJournal::journalEntryAdded( const jo_id_t _id ) m_journalEntries.push_back( _id ); m_currentJournalEntry = m_journalEntries.end(); eng()->getSongEditor()->setModified(); - printf("history size:%d\n", m_journalEntries.size()); + printf("history size: %d\n", m_journalEntries.size() ); } @@ -122,7 +123,7 @@ jo_id_t projectJournal::allocID( journallingObject * _obj ) void projectJournal::reallocID( const jo_id_t _id, journallingObject * _obj ) { - //printf("realloc %d %d\n", _id, _obj); + //printf("realloc %d %d\n", _id, _obj ); if( m_joIDs.contains( _id ) ) { m_joIDs[_id] = _obj; @@ -134,6 +135,7 @@ void projectJournal::reallocID( const jo_id_t _id, journallingObject * _obj ) void projectJournal::forgetAboutID( const jo_id_t _id ) { + printf("forget about %d\n", _id ); journalEntryVector::iterator it; while( ( it = qFind( m_journalEntries.begin(), m_journalEntries.end(), _id ) ) != m_journalEntries.end() ) @@ -150,12 +152,22 @@ void projectJournal::forgetAboutID( const jo_id_t _id ) -void projectJournal::clear( void ) +void projectJournal::clearInvalidJournallingObjects( void ) { - while( m_joIDs.size() ) + vlist::const_iterator it; + for( joIDMap::iterator it = m_joIDs.begin(); it != m_joIDs.end(); ) { - forgetAboutID( m_joIDs.keys().front() ); + if( it.data() == NULL ) + { + forgetAboutID( it.key() ); + it = m_joIDs.begin(); + } + else + { + ++it; + } } + //clearJournal(); } diff --git a/src/tracks/bb_track.cpp b/src/tracks/bb_track.cpp index a20868c26..a2b8e7153 100644 --- a/src/tracks/bb_track.cpp +++ b/src/tracks/bb_track.cpp @@ -357,7 +357,8 @@ bool FASTCALL bbTrack::play( const midiTime & _start, { if( _tco_num >= 0 ) { - return( eng()->getBBEditor()->play( _start, _start_frame, _frames, + return( eng()->getBBEditor()->play( _start, _start_frame, + _frames, _frame_base, s_infoMap[this] ) ); } @@ -424,7 +425,8 @@ void bbTrack::saveTrackSpecificSettings( QDomDocument & _doc, /* _this.setAttribute( "current", s_infoMap[this] == eng()->getBBEditor()->currentBB() );*/ if( s_infoMap[this] == 0 && - _this.parentNode().nodeName() != "clone" ) + _this.parentNode().nodeName() != "clone" && + _this.parentNode().nodeName() != "journaldata" ) { eng()->getBBEditor()->saveState( _doc, _this ); } diff --git a/src/tracks/instrument_track.cpp b/src/tracks/instrument_track.cpp index 56d3d8304..3e8343377 100644 --- a/src/tracks/instrument_track.cpp +++ b/src/tracks/instrument_track.cpp @@ -415,7 +415,9 @@ void instrumentTrack::saveSettingsBtnClicked( void ) { multimediaProject mmp( multimediaProject::INSTRUMENT_TRACK_SETTINGS ); - saveTrackSpecificSettings( mmp, mmp.content() ); + QDomElement _this = mmp.createElement( nodeName() ); + saveTrackSpecificSettings( mmp, _this ); + mmp.content().appendChild( _this ); #ifdef QT4 mmp.writeFile( sfd.selectedFiles()[0] ); #else diff --git a/src/tracks/pattern.cpp b/src/tracks/pattern.cpp index 1c47c6870..fb2bc0607 100644 --- a/src/tracks/pattern.cpp +++ b/src/tracks/pattern.cpp @@ -419,7 +419,7 @@ void pattern::saveSettings( QDomDocument & _doc, QDomElement & _this ) // pattern, we must not store actual position, instead we store -1 // which tells loadSettings() not to mess around with position if( _this.parentNode().nodeName() == "clipboard" || - _this.parentNode().nodeName() == "dnddata" ) + _this.parentNode().nodeName() == "dnddata" ) { _this.setAttribute( "pos", -1 ); }