From 3a2536e26971977b92b05bf08f04d77f81c2c5d7 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Tue, 27 Sep 2005 12:58:19 +0000 Subject: [PATCH] Added missing files because for some reason, they weren't imported at initial port... git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@7 0778d3d1-df1d-0410-868b-ea421aaaa00d --- src/core/about_dialog.cpp | 150 ++ src/core/arp_and_chords_tab_widget.cpp | 762 ++++++++ src/core/bb_editor.cpp | 457 +++++ src/core/config_mgr.cpp | 884 +++++++++ src/core/envelope_and_lfo_widget.cpp | 1118 +++++++++++ src/core/envelope_tab_widget.cpp | 631 ++++++ src/core/export_project_dialog.cpp | 481 +++++ src/core/main.cpp | 168 ++ src/core/mixer.cpp | 698 +++++++ src/core/name_label.cpp | 153 ++ src/core/note.cpp | 189 ++ src/core/note_play_handle.cpp | 259 +++ src/core/piano_roll.cpp | 2350 +++++++++++++++++++++++ src/core/piano_widget.cpp | 619 ++++++ src/core/preset_preview_play_handle.cpp | 179 ++ src/core/sample_play_handle.cpp | 106 + src/core/setup_dialog.cpp | 592 ++++++ src/core/song_editor.cpp | 1695 ++++++++++++++++ src/core/surround_area.cpp | 242 +++ src/core/timeline.cpp | 328 ++++ src/core/track.cpp | 1087 +++++++++++ src/core/track_container.cpp | 391 ++++ 22 files changed, 13539 insertions(+) create mode 100644 src/core/about_dialog.cpp create mode 100644 src/core/arp_and_chords_tab_widget.cpp create mode 100644 src/core/bb_editor.cpp create mode 100644 src/core/config_mgr.cpp create mode 100644 src/core/envelope_and_lfo_widget.cpp create mode 100644 src/core/envelope_tab_widget.cpp create mode 100644 src/core/export_project_dialog.cpp create mode 100644 src/core/main.cpp create mode 100644 src/core/mixer.cpp create mode 100644 src/core/name_label.cpp create mode 100644 src/core/note.cpp create mode 100644 src/core/note_play_handle.cpp create mode 100644 src/core/piano_roll.cpp create mode 100644 src/core/piano_widget.cpp create mode 100644 src/core/preset_preview_play_handle.cpp create mode 100644 src/core/sample_play_handle.cpp create mode 100644 src/core/setup_dialog.cpp create mode 100644 src/core/song_editor.cpp create mode 100644 src/core/surround_area.cpp create mode 100644 src/core/timeline.cpp create mode 100644 src/core/track.cpp create mode 100644 src/core/track_container.cpp diff --git a/src/core/about_dialog.cpp b/src/core/about_dialog.cpp new file mode 100644 index 000000000..4b10de6a9 --- /dev/null +++ b/src/core/about_dialog.cpp @@ -0,0 +1,150 @@ +/* + * about_dialog.cpp - implementation of about-dialog + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "about_dialog.h" +#include "embed.h" + + + + +aboutDialog::aboutDialog() : + QDialog() +{ + setWindowTitle( tr( "About LMMS..." ) ); + + m_iconLbl = new QLabel( this ); + m_iconLbl->setPixmap( embed::getIconPixmap( "icon" ) ); + m_iconLbl->setGeometry( 10, 10, 64, 64 ); + + m_appNameLbl = new QLabel( tr( "Linux MultiMedia Studio %1" + ).arg( VERSION ), this ); + m_appNameLbl->setGeometry( 80, 30, 240, 20 ); + + m_aboutTabs = new QTabWidget( this ); + + QLabel * about_lbl = new QLabel( tr( "LMMS - A powerful " + "synthesizer-studio\n\n" + "Copyright (c) 2004-2005 " + "LMMS-Developers\n\n" + "http://lmms.sourceforge.net" ) +#ifndef QT4 + , m_aboutTabs +#endif + ); + about_lbl->setAlignment( Qt::AlignVCenter | Qt::AlignLeft ); + about_lbl->setIndent( 30 ); + + + QTextEdit * authors_lbl = new QTextEdit(); + authors_lbl->setPlainText( embed::getText( "AUTHORS" ) ); + authors_lbl->setReadOnly( TRUE ); + authors_lbl->setLineWrapMode( QTextEdit::NoWrap ); + + QTextEdit * translation_lbl = new QTextEdit(); + translation_lbl->setPlainText( tr( "Current language not translated." + "\n\nIf you're interested in " + "translating LMMS in another " + "language or want to improve " + "existing translations, you're " + "welcome to help us! Just " + "contact the maintainer!" ) ); + translation_lbl->setReadOnly( TRUE ); + + + QTextEdit * copying_lbl = new QTextEdit(); + copying_lbl->setPlainText( embed::getText( "COPYING" ) ); + copying_lbl->setReadOnly( TRUE ); + copying_lbl->setLineWrapMode( QTextEdit::NoWrap ); + + + m_aboutTabs->addTab( about_lbl, tr( "About" ) ); + m_aboutTabs->addTab( authors_lbl, tr( "Authors" ) ); + m_aboutTabs->addTab( translation_lbl, tr( "Translation" ) ); + m_aboutTabs->addTab( copying_lbl, tr( "License" ) ); + + m_okBtn = new QPushButton( tr( "Close" ), this ); + connect( m_okBtn, SIGNAL( clicked() ), this, SLOT( accept() ) ); + + resize( 400, 390 ); +} + + + + +aboutDialog::~aboutDialog() +{ +} + + + + +void aboutDialog::keyPressEvent( QKeyEvent * _ke ) +{ + if( _ke->key() == Qt::Key_Escape ) + { + accept(); + } +} + + + + +void aboutDialog::resizeEvent( QResizeEvent * _re ) +{ + m_aboutTabs->setGeometry( 10, 90, _re->size().width() - 20, + _re->size().height() - 140 ); + m_okBtn->setGeometry( _re->size().width() - 110, + _re->size().height() - 40, 100, 30 ); +} + + + + +#include "about_dialog.moc" + diff --git a/src/core/arp_and_chords_tab_widget.cpp b/src/core/arp_and_chords_tab_widget.cpp new file mode 100644 index 000000000..0c6a666b0 --- /dev/null +++ b/src/core/arp_and_chords_tab_widget.cpp @@ -0,0 +1,762 @@ +/* + * arp_and_chords_tab_widget.cpp - widget for use in arp/chord-tab of + * channel-window + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#define setChecked setOn + +#endif + + +#include "arp_and_chords_tab_widget.h" +#include "embed.h" +#include "note_play_handle.h" +#include "song_editor.h" +#include "group_box.h" +#include "pixmap_button.h" +#include "knob.h" +#include "tooltip.h" + + +#ifdef HAVE_STDLIB_H +#include +#endif + + + +arpAndChordsTabWidget::chord arpAndChordsTabWidget::s_chords[] = +{ + // thanks to the FL-team for this chords *lol* ;-) took me at least 3 + // hours to get them all out of FL-arpeggiator... + + { arpAndChordsTabWidget::tr( "octave" ), { 0, -1 } }, + { arpAndChordsTabWidget::tr( "Major" ), { 0, 4, 7, -1 } }, + { arpAndChordsTabWidget::tr( "Majb5" ), { 0, 4, 6, -1 } }, + { arpAndChordsTabWidget::tr( "minor" ), { 0, 3, 7, -1 } }, + { arpAndChordsTabWidget::tr( "minb5" ), { 0, 3, 6, -1 } }, + { arpAndChordsTabWidget::tr( "sus2" ), { 0, 2, 7, -1 } }, + { arpAndChordsTabWidget::tr( "sus4" ), { 0, 5, 7, -1 } }, + { arpAndChordsTabWidget::tr( "aug" ), { 0, 4, 8, -1 } }, + { arpAndChordsTabWidget::tr( "augsus4" ), { 0, 5, 8, -1 } }, + { arpAndChordsTabWidget::tr( "tri" ), { 0, 3, 6, 9, -1 } }, + + { arpAndChordsTabWidget::tr( "6" ), { 0, 4, 7, 9, -1 } }, + { arpAndChordsTabWidget::tr( "6sus4" ), { 0, 5, 7, 9, -1 } }, + { arpAndChordsTabWidget::tr( "6add9" ), { 0, 4, 7, 12, -1 } }, + { arpAndChordsTabWidget::tr( "m6" ), { 0, 3, 7, 9, -1 } }, + { arpAndChordsTabWidget::tr( "m6add9" ), { 0, 3, 7, 9, 14, -1 } }, + + { arpAndChordsTabWidget::tr( "7" ), { 0, 4, 7, 10, -1 } }, + { arpAndChordsTabWidget::tr( "7sus4" ), { 0, 5, 7, 10, -1 } }, + { arpAndChordsTabWidget::tr( "7#5" ), { 0, 4, 8, 10, -1 } }, + { arpAndChordsTabWidget::tr( "7b5" ), { 0, 4, 6, 10, -1 } }, + { arpAndChordsTabWidget::tr( "7#9" ), { 0, 4, 7, 10, 13, 18, -1 } }, + { arpAndChordsTabWidget::tr( "7b9" ), { 0, 4, 7, 10, 13, 16, -1 } }, + { arpAndChordsTabWidget::tr( "7#5#9" ), { 0, 4, 8, 12, 14, 19, -1 } }, + { arpAndChordsTabWidget::tr( "7#5b9" ), { 0, 4, 8, 12, 14, 17, -1 } }, + { arpAndChordsTabWidget::tr( "7b5b9" ), { 0, 4, 6, 10, 12, 15, -1 } }, + { arpAndChordsTabWidget::tr( "7add11" ), { 0, 4, 7, 10, 17, -1 } }, + { arpAndChordsTabWidget::tr( "7add13" ), { 0, 4, 7, 10, 21, -1 } }, + { arpAndChordsTabWidget::tr( "7#11" ), { 0, 4, 7, 10, 18, -1 } }, + { arpAndChordsTabWidget::tr( "Maj7" ), { 0, 4, 7, 11, -1 } }, + { arpAndChordsTabWidget::tr( "Maj7b5" ), { 0, 4, 6, 11, -1 } }, + { arpAndChordsTabWidget::tr( "Maj7#5" ), { 0, 4, 8, 11, -1 } }, + { arpAndChordsTabWidget::tr( "Maj7#11" ), { 0, 4, 7, 11, 18, -1 } }, + { arpAndChordsTabWidget::tr( "Maj7add13" ), { 0, 4, 7, 11, 21, -1 } }, + { arpAndChordsTabWidget::tr( "m7" ), { 0, 3, 7, 10, -1 } }, + { arpAndChordsTabWidget::tr( "m7b5" ), { 0, 3, 6, 10, -1 } }, + { arpAndChordsTabWidget::tr( "m7b9" ), { 0, 3, 7, 10, 13, -1 } }, + { arpAndChordsTabWidget::tr( "m7add11" ), { 0, 3, 7, 10, 17, -1 } }, + { arpAndChordsTabWidget::tr( "m7add13" ), { 0, 3, 7, 10, 21, -1 } }, + { arpAndChordsTabWidget::tr( "m-Maj7" ), { 0, 3, 7, 11, -1 } }, + { arpAndChordsTabWidget::tr( "m-Maj7add11" ), { 0, 3, 7, 11, 17, -1 } }, + { arpAndChordsTabWidget::tr( "m-Maj7add13" ), { 0, 3, 7, 11, 21, -1 } }, + + { arpAndChordsTabWidget::tr( "9" ), { 0, 4, 7, 10, 14, -1 } }, + { arpAndChordsTabWidget::tr( "9sus4" ), { 0, 5, 7, 10, 14, -1 } }, + { arpAndChordsTabWidget::tr( "add9" ), { 0, 4, 7, 14, -1 } }, + { arpAndChordsTabWidget::tr( "9#5" ), { 0, 4, 8, 10, 14, -1 } }, + { arpAndChordsTabWidget::tr( "9b5" ), { 0, 4, 6, 10, 14, -1 } }, + { arpAndChordsTabWidget::tr( "9#11" ), { 0, 4, 7, 10, 14, 18, -1 } }, + { arpAndChordsTabWidget::tr( "9b13" ), { 0, 4, 7, 10, 14, 20, -1 } }, + { arpAndChordsTabWidget::tr( "Maj9" ), { 0, 4, 7, 11, 14, -1 } }, + { arpAndChordsTabWidget::tr( "Maj9sus4" ), { 0, 5, 7, 11, 15, -1 } }, + { arpAndChordsTabWidget::tr( "Maj9#5" ), { 0, 4, 8, 11, 14, -1 } }, + { arpAndChordsTabWidget::tr( "Maj9#11" ), { 0, 4, 7, 11, 14, 18, -1 } }, + { arpAndChordsTabWidget::tr( "m9" ), { 0, 3, 7, 10, 14, -1 } }, + { arpAndChordsTabWidget::tr( "madd9" ), { 0, 3, 7, 14, -1 } }, + { arpAndChordsTabWidget::tr( "m9b5" ), { 0, 3, 6, 10, 14, -1 } }, + { arpAndChordsTabWidget::tr( "m9-Maj7" ), { 0, 3, 7, 11, 14, -1 } }, + + { arpAndChordsTabWidget::tr( "11" ), { 0, 4, 7, 10, 14, 17, -1 } }, + { arpAndChordsTabWidget::tr( "11b9" ), { 0, 4, 7, 10, 13, 17, -1 } }, + { arpAndChordsTabWidget::tr( "Maj11" ), { 0, 4, 7, 11, 14, 17, -1 } }, + { arpAndChordsTabWidget::tr( "m11" ), { 0, 3, 7, 10, 14, 17, -1 } }, + { arpAndChordsTabWidget::tr( "m-Maj11" ), { 0, 3, 7, 11, 14, 17, -1 } }, + + { arpAndChordsTabWidget::tr( "13" ), { 0, 4, 7, 10, 14, 21, -1 } }, + { arpAndChordsTabWidget::tr( "13#9" ), { 0, 4, 7, 10, 15, 21, -1 } }, + { arpAndChordsTabWidget::tr( "13b9" ), { 0, 4, 7, 10, 13, 21, -1 } }, + { arpAndChordsTabWidget::tr( "13b5b9" ), { 0, 4, 6, 10, 13, 21, -1 } }, + { arpAndChordsTabWidget::tr( "Maj13" ), { 0, 4, 7, 11, 14, 21, -1 } }, + { arpAndChordsTabWidget::tr( "m13" ), { 0, 3, 7, 10, 14, 21, -1 } }, + { arpAndChordsTabWidget::tr( "m-Maj13" ), { 0, 3, 7, 11, 14, 21, -1 } }, + + { arpAndChordsTabWidget::tr( "Major" ), { 0, 2, 4, 5, 7, 9, 11, -1 } }, + { arpAndChordsTabWidget::tr( "Harmonic minor" ), { 0, 2, 3, 5, 7, 8, + 11, -1 } }, + { arpAndChordsTabWidget::tr( "Melodic minor" ), { 0, 2, 3, 5, 7, 9, + 11, -1 } }, + { arpAndChordsTabWidget::tr( "Whole tone" ), { 0, 2, 4, 6, 8, 10, + -1 } }, + { arpAndChordsTabWidget::tr( "Diminished" ), { 0, 2, 3, 5, 6, 8, 9, + 11, -1 } }, + { arpAndChordsTabWidget::tr( "Major pentatonic" ), { 0, 2, 4, 7, 10, + -1 } }, + { arpAndChordsTabWidget::tr( "Minor pentatonic" ), { 0, 3, 5, 7, 10, + -1 } }, + { arpAndChordsTabWidget::tr( "Jap in sen" ), { 0, 1, 5, 7, 10, -1 } }, + { arpAndChordsTabWidget::tr( "Major bebop" ), { 0, 2, 4, 5, 7, 8, 9, + 11, -1 } }, + { arpAndChordsTabWidget::tr( "Dominant bebop" ), { 0, 2, 4, 5, 7, 9, + 10, 11, -1 } }, + { arpAndChordsTabWidget::tr( "Blues" ), { 0, 3, 5, 6, 7, 10, -1 } }, + { arpAndChordsTabWidget::tr( "Arabic" ), { 0, 1, 4, 5, 7, 8, 11, -1 } }, + { arpAndChordsTabWidget::tr( "Enigmatic" ), { 0, 1, 4, 6, 8, 10, 11, + -1 } }, + { arpAndChordsTabWidget::tr( "Neopolitan" ), { 0, 1, 3, 5, 7, 9, 11, + -1 } }, + { arpAndChordsTabWidget::tr( "Neopolitan minor" ), { 0, 1, 3, 5, 7, 9, + 11, -1 } }, + { arpAndChordsTabWidget::tr( "Hungarian minor" ), { 0, 2, 3, 6, 7, 9, + 11, -1 } }, + { arpAndChordsTabWidget::tr( "Dorian" ), { 0, 2, 3, 5, 7, 9, 10, -1 } }, + { arpAndChordsTabWidget::tr( "Phrygolydian" ), { 0, 1, 3, 5, 7, 8, 10, + -1 } }, + { arpAndChordsTabWidget::tr( "Lydian" ), { 0, 2, 4, 6, 7, 9, 11, -1 } }, + { arpAndChordsTabWidget::tr( "Mixolydian" ), { 0, 2, 4, 5, 7, 9, 10, + -1 } }, + { arpAndChordsTabWidget::tr( "Aeolian" ), { 0, 2, 3, 5, 7, 8, 10, + -1 } }, + { arpAndChordsTabWidget::tr( "Locrian" ), { 0, 1, 3, 5, 6, 8, 10, + -1 } }, + + { "", { -1, -1 } } + +} ; + + +const int CHORDS_GROUPBOX_X = 5; +const int CHORDS_GROUPBOX_Y = 5; +const int CHORDS_GROUPBOX_WIDTH = 240; +const int CHORDS_GROUPBOX_HEIGHT = 65; +const int ARP_GROUPBOX_X = CHORDS_GROUPBOX_X; +const int ARP_GROUPBOX_Y = 10 + CHORDS_GROUPBOX_Y + CHORDS_GROUPBOX_HEIGHT; +const int ARP_GROUPBOX_WIDTH = CHORDS_GROUPBOX_WIDTH; +const int ARP_GROUPBOX_HEIGHT = 180 - ARP_GROUPBOX_Y; + + + +arpAndChordsTabWidget::arpAndChordsTabWidget( channelTrack * _channel_track, + QWidget * _parent ) : + QWidget( _parent ), + settings(), + m_arpDirection( UP ), + m_channelTrack( _channel_track ) +{ + m_chordsGroupBox = new groupBox( tr( "CHORDS" ), this ); + m_chordsGroupBox->setGeometry( CHORDS_GROUPBOX_X, CHORDS_GROUPBOX_Y, + CHORDS_GROUPBOX_WIDTH, + CHORDS_GROUPBOX_HEIGHT ); + + m_chordsComboBox = new QComboBox( m_chordsGroupBox ); + m_chordsComboBox->setFont( pointSize<9>( m_chordsComboBox->font() ) ); + m_chordsComboBox->setGeometry( 10, 25, 100, 22 ); + + for( int i = 0; s_chords[i].interval[0] != -1; ++i ) + { + m_chordsComboBox->addItem( tr( s_chords[i].name +#ifdef QT4 + .toAscii().constData() +#endif + ) ); + } + + m_chordRangeKnob = new knob( knobBright_26, m_chordsGroupBox, + tr( "Chord range" ) ); + m_chordRangeKnob->setLabel( tr( "RANGE" ) ); + m_chordRangeKnob->setRange( 1.0, 9.0, 1.0 ); + m_chordRangeKnob->setValue( 1.0, TRUE ); + m_chordRangeKnob->move( 123, 23 ); + m_chordRangeKnob->setHintText( tr( "Chord range:" ) + " ", " " + + tr( "octave(s)" ) ); +#ifdef QT4 + m_chordRangeKnob->setWhatsThis( +#else + QWhatsThis::add( m_chordRangeKnob, +#endif + tr( "Use this knob for setting the chord range in octaves. " + "The selected chord will be played within specified " + "amount of octaves." ) ); + + + + + m_arpGroupBox = new groupBox( tr( "ARPEGGIO" ), this ); + m_arpGroupBox->setGeometry( ARP_GROUPBOX_X, ARP_GROUPBOX_Y, + ARP_GROUPBOX_WIDTH, + ARP_GROUPBOX_HEIGHT ); + +#ifdef QT4 + m_arpGroupBox->setWhatsThis( +#else + QWhatsThis::add( m_arpGroupBox, +#endif + tr( "An arpeggio is a type of playing (especially plucked) " + "instruments, which makes the music much livelier. " + "The strings of such instruments (e.g. harps) are " + "plucked like chords, the only difference is, that " + "this is done in a sequential order, so the notes are " + "not played at the same time. Typical arpeggios are " + "major or minor triads. But there're a lot of other " + "possible chords, you can select." ) ); + m_arpComboBox = new QComboBox( m_arpGroupBox ); + m_arpComboBox->setFont( pointSize<9>( m_arpComboBox->font() ) ); + m_arpComboBox->setGeometry( 10, 25, 100, 22 ); + + for( int i = 0; s_chords[i].interval[0] != -1; ++i ) + { + m_arpComboBox->addItem( tr( s_chords[i].name +#ifdef QT4 + .toAscii().constData() +#endif + ) ); + } + m_arpRangeKnob = new knob( knobBright_26, m_arpGroupBox, + tr( "Arpeggio range" ) ); + m_arpRangeKnob->setLabel( tr( "RANGE" ) ); + m_arpRangeKnob->setRange( 1.0, 9.0, 1.0 ); + m_arpRangeKnob->setValue( 1.0, TRUE ); + m_arpRangeKnob->move( 124, 24 ); + m_arpRangeKnob->setHintText( tr( "Arpeggio range:" ) + " ", " " + + tr( "octave(s)" ) ); +#ifdef QT4 + m_arpRangeKnob->setWhatsThis( +#else + QWhatsThis::add( m_arpRangeKnob, +#endif + tr( "Use this knob for setting the arpeggio range in octaves. " + "The selected arpeggio will be played within specified " + "amount of octaves." ) ); + + m_arpTimeKnob = new knob( knobBright_26, m_arpGroupBox, + tr( "Arpeggio time" ) ); + m_arpTimeKnob->setLabel( tr( "TIME" ) ); + m_arpTimeKnob->setRange( 10.0, 1000.0, 1.0 ); + m_arpTimeKnob->setValue( 100.0, TRUE ); + m_arpTimeKnob->move( 164, 24 ); + m_arpTimeKnob->setHintText( tr( "Arpeggio time:" ) + " ", " " + + tr( "ms" ) ); +#ifdef QT4 + m_arpTimeKnob->setWhatsThis( +#else + QWhatsThis::add( m_arpTimeKnob, +#endif + tr( "Use this knob for setting the arpeggio time in " + "milliseconds. The arpeggio time specifies how long " + "each arpeggio-tone should be played." ) ); + + m_arpGateKnob = new knob( knobBright_26, m_arpGroupBox, + tr( "Arpeggio gate" ) ); + m_arpGateKnob->setLabel( tr( "GATE" ) ); + m_arpGateKnob->setRange( 1.0, 200.0, 1.0 ); + m_arpGateKnob->setValue( 100.0, TRUE ); + m_arpGateKnob->move( 204, 24 ); + m_arpGateKnob->setHintText( tr( "Arpeggio gate:" ) + " ", tr( "%" ) ); +#ifdef QT4 + m_arpGateKnob->setWhatsThis( +#else + QWhatsThis::add( m_arpGateKnob, +#endif + tr( "Use this knob for setting the arpeggio gate. The " + "arpeggio gate specifies the percent of a whole " + "arpeggio-tone that should be played. With this you " + "can make cool staccato-arpeggios." ) ); + + m_arpDirectionLbl = new QLabel( tr( "DIRECTION:" ), m_arpGroupBox ); + m_arpDirectionLbl->setGeometry( 10, 70, 64, 8 ); + m_arpDirectionLbl->setFont( pointSize<6>( m_arpDirectionLbl->font() ) ); + + +/* m_arpOffBtn = new pixmapButton( m_arpGroupBox ); + m_arpOffBtn->move( 10, 87 ); + m_arpOffBtn->setActiveGraphic( embed::getIconPixmap( "arp_off_on" ) ); + m_arpOffBtn->setInactiveGraphic( embed::getIconPixmap( + "arp_off_off" ) ); +#ifdef QT4 + m_arpOffBtn->setChecked( TRUE ); +#else + m_arpOffBtn->setOn( TRUE ); +#endif +#ifndef QT4 + m_arpOffBtn->setBackgroundMode( Qt::PaletteBackground ); +#endif +#ifdef QT4 + m_arpOffBtn->setToolTip( +#else + toolTip::add( m_arpOffBtn, +#endif + tr( "arpeggio direction = none = no arpeggio" ) ); + connect( m_arpOffBtn, SIGNAL( toggled( bool ) ), this, + SLOT( arpOffToggled( bool ) ) );*/ + + + m_arpUpBtn = new pixmapButton( m_arpGroupBox ); + m_arpUpBtn->move( 80, 70 ); + m_arpUpBtn->setActiveGraphic( embed::getIconPixmap( "arp_up_on" ) ); + m_arpUpBtn->setInactiveGraphic( embed::getIconPixmap( "arp_up_off" ) ); +#ifdef QT4 + m_arpUpBtn->setChecked( TRUE ); +#else + m_arpUpBtn->setOn( TRUE ); +#endif +#ifndef QT4 + m_arpUpBtn->setBackgroundMode( Qt::PaletteBackground ); +#endif + toolTip::add( m_arpUpBtn, tr( "arpeggio direction = up" ) ); + connect( m_arpUpBtn, SIGNAL( toggled( bool ) ), this, + SLOT( arpUpToggled( bool ) ) ); + + m_arpDownBtn = new pixmapButton( m_arpGroupBox ); + m_arpDownBtn->move( 100, 70 ); + m_arpDownBtn->setActiveGraphic( embed::getIconPixmap( "arp_down_on" ) ); + m_arpDownBtn->setInactiveGraphic( embed::getIconPixmap( + "arp_down_off" ) ); +#ifndef QT4 + m_arpDownBtn->setBackgroundMode( Qt::PaletteBackground ); +#endif + toolTip::add( m_arpDownBtn, tr( "arpeggio direction = down" ) ); + connect( m_arpDownBtn, SIGNAL( toggled( bool ) ), this, + SLOT( arpDownToggled( bool ) ) ); + + m_arpUpAndDownBtn = new pixmapButton( m_arpGroupBox ); + m_arpUpAndDownBtn->move( 120, 70 ); + m_arpUpAndDownBtn->setActiveGraphic( embed::getIconPixmap( + "arp_up_and_down_on" ) ); + m_arpUpAndDownBtn->setInactiveGraphic( embed::getIconPixmap( + "arp_up_and_down_off" ) ); +#ifndef QT4 + m_arpUpAndDownBtn->setBackgroundMode( Qt::PaletteBackground ); +#endif + toolTip::add( m_arpUpAndDownBtn, + tr( "arpeggio direction = up and down" ) ); + connect( m_arpUpAndDownBtn, SIGNAL( toggled( bool ) ), this, + SLOT( arpUpAndDownToggled( bool ) ) ); + + m_arpRandomBtn = new pixmapButton( m_arpGroupBox ); + m_arpRandomBtn->move( 140, 70 ); + m_arpRandomBtn->setActiveGraphic( embed::getIconPixmap( + "arp_random_on" ) ); + m_arpRandomBtn->setInactiveGraphic( embed::getIconPixmap( + "arp_random_off" ) ); +#ifndef QT4 + m_arpRandomBtn->setBackgroundMode( Qt::PaletteBackground ); +#endif + toolTip::add( m_arpRandomBtn, tr( "arpeggio direction = random" ) ); + connect( m_arpRandomBtn, SIGNAL( toggled( bool ) ), this, + SLOT( arpRandomToggled( bool ) ) ); + + QButtonGroup * m_arpDirections_group = new QButtonGroup( this ); +// m_arpDirections_group->addButton( m_arpOffBtn ); + m_arpDirections_group->addButton( m_arpUpBtn ); + m_arpDirections_group->addButton( m_arpDownBtn ); + m_arpDirections_group->addButton( m_arpUpAndDownBtn ); + m_arpDirections_group->addButton( m_arpRandomBtn ); + m_arpDirections_group->setExclusive( TRUE ); +#ifndef QT4 + m_arpDirections_group->hide(); +#endif +} + + + + +arpAndChordsTabWidget::~arpAndChordsTabWidget() +{ +} + + + + +void arpAndChordsTabWidget::processNote( notePlayHandle * _n ) +{ + const int base_note_key = _n->key(); + // we add chord-subnotes to note if either note is a base-note and + // arpeggio is not used or note is part of an arpeggio + // at the same time we only add sub-notes if nothing of the note was + // played yet, because otherwise we would add chord-subnotes every + // time an audio-buffer is rendered... + if( ( ( _n->baseNote() && m_arpGroupBox->isActive() == FALSE ) || + _n->arpNote() ) && + _n->totalFramesPlayed() == 0 && + m_chordsGroupBox->isActive() == TRUE ) + { + // then insert sub-notes for chord +#ifdef QT4 + const int selected_chord = m_chordsComboBox->currentIndex(); +#else + const int selected_chord = m_chordsComboBox->currentItem(); +#endif + for( int octave_cnt = 0; + octave_cnt < m_chordRangeKnob->value(); ++octave_cnt ) + { + const int sub_note_key_base = base_note_key + + octave_cnt * NOTES_PER_OCTAVE; + // if octave_cnt == 1 we're in the first octave and + // the base-note is already done, so we don't have to + // create it in the following loop, then we loop until + // there's a -1 in the interval-array + for( int i = ( octave_cnt == 0 ) ? 1 : 0; + s_chords[selected_chord].interval[i] != -1; + ++i ) + { + // add interval to sub-note-key + const int sub_note_key = sub_note_key_base + + (int) s_chords[ + selected_chord].interval[i]; + // maybe we're out of range -> let's get outta + // here! + if( sub_note_key > NOTES_PER_OCTAVE*OCTAVES ) + { + break; + } + // create copy of base-note + note note_copy( 0, 0, (tones)( sub_note_key % + NOTES_PER_OCTAVE ), + (octaves)( sub_note_key / + NOTES_PER_OCTAVE ), + _n->getVolume(), + _n->getPanning() ); + // duplicate note-play-handle, only note is + // different + notePlayHandle * note_play_handle = + new notePlayHandle( + _n->getChannelTrack(), + _n->framesAhead(), + _n->frames(), ¬e_copy ); + // add sub-note to base-note, now all stuff is + // done by notePlayHandle::play_note() + _n->addSubNote( note_play_handle ); + } + } + } + + + // now follows code for arpeggio + + if( _n->baseNote() == FALSE || m_arpDirection == OFF || + !m_arpGroupBox->isActive() || + ( _n->released() && _n->releaseFramesDone() >= + _n->actualReleaseFramesToDo() ) ) + { + return; + } + + +#ifdef QT4 + const int selected_arp = m_arpComboBox->currentIndex(); +#else + const int selected_arp = m_arpComboBox->currentItem(); +#endif + + const int cur_chord_size = getChordSize( s_chords[selected_arp] ); + const int total_range = (int)( cur_chord_size * + m_arpRangeKnob->value() ); + // number of frames that every note should be played + const Uint32 arp_frames = (Uint32)( m_arpTimeKnob->value() / 1000.0f * + mixer::inst()->sampleRate() ); + const Uint32 gated_frames = (Uint32)( m_arpGateKnob->value() * + arp_frames / 100.0f ); + // used for calculating remaining frames for arp-note, we have to add + // arp_frames-1, otherwise the first arp-note will not be setup + // correctly... -> arp_frames frames silence at the start of every note! + int cur_frame = _n->totalFramesPlayed() + arp_frames - 1; + // used for loop + Uint32 frames_processed = 0; + // len for all arp-notes (depending on arp-time) + + while( frames_processed < mixer::inst()->framesPerAudioBuffer() ) + { + const Uint32 remaining_frames_for_cur_arp = arp_frames - + ( cur_frame % arp_frames ); + // does current arp-note fill whole audio-buffer? + if( remaining_frames_for_cur_arp >= + mixer::inst()->framesPerAudioBuffer() ) + { + // then we don't have to do something! + break; + } + + frames_processed += remaining_frames_for_cur_arp; + + // init with zero + int cur_arp_idx = 0; + + // process according to arpeggio-direction... + if( m_arpDirection == UP ) + { + cur_arp_idx = ( cur_frame / arp_frames ) % total_range; + } + else if( m_arpDirection == DOWN ) + { + cur_arp_idx = total_range - ( cur_frame / arp_frames ) % + total_range - 1; + } + else if( m_arpDirection == UP_AND_DOWN && total_range > 1 ) + { + // imagine, we had to play the arp once up and then + // once down -> makes 2 * total_range possible notes... + // because we don't play the lower and upper notes + // twice, we have to subtract 2 + cur_arp_idx = ( cur_frame / arp_frames ) % + ( total_range * 2 - 2 ); + // if greater than total_range, we have to play down... + // looks like the code for arp_dir==DOWN... :) + if( cur_arp_idx >= total_range ) + { + cur_arp_idx = total_range - cur_arp_idx % + ( total_range - 1 ) - 1; + } + } + else if( m_arpDirection == RANDOM ) + { + // just pick a random chord-index + cur_arp_idx = (int)( total_range * ( (float) rand() / + (float) RAND_MAX ) ); + } + + // now calculate final key for our arp-note + const int sub_note_key = base_note_key + (cur_arp_idx / + cur_chord_size ) * + NOTES_PER_OCTAVE + + s_chords[selected_arp].interval[cur_arp_idx % cur_chord_size]; + + // range-checking + if( sub_note_key >= NOTES_PER_OCTAVE * OCTAVES || + sub_note_key < 0 ) + { + continue; + } + + float vol_level = 1.0f; + if( _n->released() ) + { + vol_level = _n->volumeLevel( cur_frame + gated_frames ); + } + + // create new arp-note + note new_note( midiTime( 0 ), midiTime( 0 ), + static_cast( sub_note_key % + NOTES_PER_OCTAVE ), + static_cast( sub_note_key / + NOTES_PER_OCTAVE ), + static_cast( _n->getVolume() * + vol_level ), + _n->getPanning() ); + + // duplicate note-play-handle, only ptr to note is different + // and is_arp_note=TRUE + notePlayHandle * note_play_handle = new notePlayHandle( + _n->getChannelTrack(), + _n->framesAhead() + + frames_processed, + gated_frames, + &new_note, + TRUE ); + + // add sub-note to base-note, now all stuff is done by + // notePlayHandle::play_note() + _n->addSubNote( note_play_handle ); + + // update counters + frames_processed += arp_frames; + cur_frame += arp_frames; + } +} + + + + +void arpAndChordsTabWidget::saveSettings( QDomDocument & _doc, + QDomElement & _parent ) +{ + QDomElement elw_de = _doc.createElement( nodeName() ); + elw_de.setAttribute( "chorddisabled", QString::number( + !m_chordsGroupBox->isActive() ) ); +#ifdef QT4 + elw_de.setAttribute( "chord", QString::number( + m_chordsComboBox->currentIndex() ) ); +#else + elw_de.setAttribute( "chord", QString::number( + m_chordsComboBox->currentItem() ) ); +#endif + elw_de.setAttribute( "chordrange", QString::number( + m_chordRangeKnob->value() ) ); + + elw_de.setAttribute( "arpdisabled", QString::number( + !m_arpGroupBox->isActive() ) ); +#ifdef QT4 + elw_de.setAttribute( "arp", QString::number( + m_arpComboBox->currentIndex() ) ); +#else + elw_de.setAttribute( "arp", QString::number( + m_arpComboBox->currentItem() ) ); +#endif + elw_de.setAttribute( "arprange", QString::number( + m_arpRangeKnob->value() ) ); + elw_de.setAttribute( "arptime", QString::number( + m_arpTimeKnob->value() ) ); + elw_de.setAttribute( "arpgate", QString::number( + m_arpGateKnob->value() ) ); + elw_de.setAttribute( "arpdir", QString::number( + m_arpDirection ) ); + + _parent.appendChild( elw_de ); +} + + + + +void arpAndChordsTabWidget::loadSettings( const QDomElement & _this ) +{ + m_chordsGroupBox->setState( !_this.attribute + ( "chorddisabled" ).toInt() ); +#ifdef QT4 + m_chordsComboBox->setCurrentIndex( _this.attribute( "chord" ).toInt() ); +#else + m_chordsComboBox->setCurrentItem( _this.attribute( "chord" ).toInt() ); +#endif + m_chordRangeKnob->setValue( _this.attribute( "chordrange" ).toFloat() ); +#ifdef QT4 + m_arpComboBox->setCurrentIndex( _this.attribute( "arp" ).toInt() ); +#else + m_arpComboBox->setCurrentItem( _this.attribute( "arp" ).toInt() ); +#endif + m_arpRangeKnob->setValue( _this.attribute( "arprange" ).toFloat() ); + m_arpTimeKnob->setValue( _this.attribute( "arptime" ).toFloat() ); + m_arpGateKnob->setValue( _this.attribute( "arpgate" ).toFloat() ); + m_arpDirection = static_cast( + _this.attribute( "arpdir" ).toInt() ); + + m_arpGroupBox->setState( m_arpDirection != OFF && + !_this.attribute( "arpdisabled" ).toInt() ); + switch( m_arpDirection ) + { + case DOWN: + m_arpDownBtn->setChecked( TRUE ); + break; + case UP_AND_DOWN: + m_arpUpAndDownBtn->setChecked( TRUE ); + break; + case RANDOM: + m_arpRandomBtn->setChecked( TRUE ); + break; + case UP: + default: + m_arpUpBtn->setChecked( TRUE ); + m_arpDirection = UP; + break; + } +} + + + + +void arpAndChordsTabWidget::arpUpToggled( bool _on ) +{ + if( _on ) + { + m_arpDirection = UP; + } + songEditor::inst()->setModified(); +} + + + + +void arpAndChordsTabWidget::arpDownToggled( bool _on ) +{ + if( _on ) + { + m_arpDirection = DOWN; + } + songEditor::inst()->setModified(); +} + + + + +void arpAndChordsTabWidget::arpUpAndDownToggled( bool _on ) +{ + if( _on ) + { + m_arpDirection = UP_AND_DOWN; + } + songEditor::inst()->setModified(); +} + + + + +void arpAndChordsTabWidget::arpRandomToggled( bool _on ) +{ + if( _on ) + { + m_arpDirection = RANDOM; + } + songEditor::inst()->setModified(); +} + + + + +#undef setChecked + +#include "arp_and_chords_tab_widget.moc" + diff --git a/src/core/bb_editor.cpp b/src/core/bb_editor.cpp new file mode 100644 index 000000000..fe3fbad95 --- /dev/null +++ b/src/core/bb_editor.cpp @@ -0,0 +1,457 @@ +/* + * bb_editor.cpp - basic main-window for editing of beats and basslines + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include + +#endif + + +#include "bb_editor.h" +#include "song_editor.h" +#include "embed.h" +#include "pixmap_button.h" +#include "track_container.h" +#include "bb_track.h" +#include "name_label.h" +#include "templates.h" +#include "debug.h" +#include "spc_bg_hndl_widget.h" +#include "tooltip.h" + + + +const int BBE_PPT = 192; + + + +bbEditor * bbEditor::s_instanceOfMe = NULL; +QPixmap * bbEditor::s_titleArtwork = NULL; + + + +bbEditor::bbEditor() : + trackContainer() +{ + setWindowIcon( embed::getIconPixmap( "bb_track" ) ); + setWindowTitle( tr( "Beat+Bassline Editor" ) ); + setMinimumWidth( TRACK_OP_WIDTH + DEFAULT_SETTINGS_WIDGET_WIDTH + + BBE_PPT + 2 * TCO_BORDER_WIDTH ); + setGeometry( 10, 340, minimumWidth(), 300 ); + show(); + + if( s_titleArtwork == NULL ) + { + s_titleArtwork = new QPixmap( embed::getIconPixmap( + "bb_editor_title_artwork" ) ); + } + + containerWidget()->move( 0, 47 ); + setPixelsPerTact( BBE_PPT ); + updateBackground(); + + m_playButton = new pixmapButton( this ); + m_playButton->move( 96, 7 ); + m_playButton->setCheckable( FALSE ); + m_playButton->setActiveGraphic( embed::getIconPixmap( "play" ) ); + m_playButton->setInactiveGraphic( embed::getIconPixmap( "play" ) ); + m_playButton->setBgGraphic( specialBgHandlingWidget::getBackground( + m_playButton ) ); + connect( m_playButton, SIGNAL( clicked() ), this, SLOT( play() ) ); + + m_stopButton = new pixmapButton( this ); + m_stopButton->move( 136, 7 ); + m_stopButton->setCheckable( FALSE ); + m_stopButton->setActiveGraphic( embed::getIconPixmap( "stop" ) ); + m_stopButton->setInactiveGraphic( embed::getIconPixmap( "stop" ) ); + m_stopButton->setBgGraphic( specialBgHandlingWidget::getBackground( + m_playButton ) ); + connect( m_stopButton, SIGNAL( clicked() ), this, SLOT( stop() ) ); + + + toolTip::add( m_playButton, + tr( "Play/pause current beat/bassline (Space)" ) ); + toolTip::add( m_stopButton, + tr( "Stop playing of current beat/bassline (Space)" ) ); +#ifdef QT4 + m_playButton->setWhatsThis( +#else + QWhatsThis::add( m_playButton, +#endif + tr( "Click here, if you want to play the current " + "beat/bassline. The beat/bassline is automatically " + "looped when its end is reached." ) ); +#ifdef QT4 + m_stopButton->setWhatsThis( +#else + QWhatsThis::add( m_stopButton, +#endif + tr( "Click here, if you want to stop playing of current " + "beat/bassline." ) ); + +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif +} + + + + +bbEditor::~bbEditor() +{ +} + + + + +csize bbEditor::currentBB( void ) const +{ + return( static_cast( currentPosition().getTact() ) ); +} + + + + +void bbEditor::setCurrentBB( csize _bb ) +{ + // first make sure, all channels have a TCO at current BB + createTCOsForBB( _bb ); + + realignTracks(); + + // now update all track-labels (the current one has to become white, + // the others green) + for( csize i = 0; i < bbEditor::inst()->numOfBBs(); ++i ) + { + bbTrack::findBBTrack( i )->trackLabel()->update(); + } + + emit positionChanged( m_currentPosition = midiTime( _bb, 0 ) ); +} + + + + +tact bbEditor::lengthOfBB( csize _bb ) +{ + midiTime max_length; + + trackVector tv = tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + trackContentObject * tco = ( *it )->getTCO( _bb ); +#ifdef LMMS_DEBUG + assert( tco != NULL ); +#endif + max_length = tMax( max_length, tco->length() ); + } + if( max_length.getTact64th() == 0 ) + { + return( max_length.getTact() ); + } + + return( max_length.getTact() + 1 ); +} + + + + +bool FASTCALL bbEditor::play( midiTime _start, Uint32 _start_frame, + Uint32 _frames, Uint32 _frame_base, + Sint16 _tco_num ) +{ + bool played_a_note = FALSE; + if( lengthOfBB( _tco_num ) <= 0 ) + { + return( played_a_note ); + } + + _start = ( _start.getTact() % lengthOfBB( _tco_num ) ) * 64 + + _start.getTact64th(); + trackVector tv = tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + if( ( *it )->play( _start, _start_frame, _frames, _frame_base, + _tco_num ) == TRUE ) + { + played_a_note = TRUE; + } + } + + return( played_a_note ); +} + + + + +csize bbEditor::numOfBBs( void ) const +{ + return( songEditor::inst()->countTracks( track::BB_TRACK ) ); +} + + + + +void bbEditor::removeBB( csize _bb ) +{ + trackVector tv = tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + ( *it )->removeTCO( _bb ); + ( *it )->getTrackContentWidget()->removeTact( _bb * 64 ); + } +/* if( _bb == currentBB() && numOfBBs() > 0 ) + {*/ + if( _bb > 0) + { + setCurrentBB( _bb - 1 ); + } + else + { + setCurrentBB( 0 ); + } +// } +} + + + +// close-handler for bb-editor-window because closing of bb-editor isn't allowed +// instead of closing it's being hidden +void bbEditor::closeEvent( QCloseEvent * _ce ) +{ + _ce->ignore(); + hide(); +} + + + + +/*void bbEditor::paintEvent( QPaintEvent * ) +{ +}*/ + + + + +void bbEditor::keyPressEvent( QKeyEvent * _ke ) +{ + if ( _ke->key() == Qt::Key_Space ) + { + if( songEditor::inst()->playing() ) + { + stop (); + } + else + { + play (); + } + } + else if ( _ke->key() == Qt::Key_Plus ) + { + if( currentBB() + 1 < numOfBBs() ) + { + setCurrentBB( currentBB() + 1 ); + } + } + else if ( _ke->key() == Qt::Key_Minus ) + { + if( currentBB() > 0 ) + { + setCurrentBB( currentBB() - 1 ); + } + } + else + { + // ignore event and pass to parent-widget + _ke->ignore(); + } + +} + + + + +void bbEditor::wheelEvent( QWheelEvent * _we ) +{ + lmmsMainWin::inst()->workspace()->wheelEvent( _we ); +} + + + + +void bbEditor::resizeEvent( QResizeEvent * _re ) +{ + updateBackground(); + setPixelsPerTact( width() - ( TRACK_OP_WIDTH + + DEFAULT_SETTINGS_WIDGET_WIDTH + 2 * + TCO_BORDER_WIDTH ) ); + trackContainer::resizeEvent( _re ); +} + + + + +void bbEditor::updateBackground( void ) +{ + QPixmap draw_pm( size() ); +#ifdef QT4 + draw_pm.fill( containerWidget()->palette().brush( + containerWidget()->backgroundRole() ).color() ); +#else + draw_pm.fill( containerWidget()->paletteBackgroundColor() ); +#endif + + QPainter p( &draw_pm ); + + p.fillRect( 0, 0, width(), s_titleArtwork->height(), + QColor( 74, 125, 213 ) ); + p.drawPixmap( 0, 0, *s_titleArtwork ); +#ifdef QT4 + QPalette pal = palette(); + pal.setBrush( backgroundRole(), QBrush( draw_pm ) ); + setPalette( pal ); +#else + setErasePixmap( draw_pm ); +#endif +} + + + +void bbEditor::play( void ) +{ + if( songEditor::inst()->playing() ) + { + if( songEditor::inst()->playMode() != songEditor::PLAY_BB ) + { + songEditor::inst()->stop(); + songEditor::inst()->playBB(); + m_playButton->setInactiveGraphic( + embed::getIconPixmap( "pause" ) ); + } + else + { + songEditor::inst()->pause(); + m_playButton->setInactiveGraphic( + embed::getIconPixmap("play") ); + } + } + else if( songEditor::inst()->paused() ) + { + songEditor::inst()->resumeFromPause(); + m_playButton->setInactiveGraphic( + embed::getIconPixmap( "pause" ) ); + } + else + { + m_playButton->setInactiveGraphic( + embed::getIconPixmap( "pause" ) ); + songEditor::inst()->playBB(); + } + +} + + + + +void bbEditor::stop( void ) +{ + songEditor::inst()->stop(); + m_playButton->setInactiveGraphic( embed::getIconPixmap( "play" ) ); + m_playButton->update(); +} + + + + + + +void bbEditor::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + trackContainer::saveSettings( _doc, _parent ); +} + + + + +void bbEditor::loadSettings( const QDomElement & _this ) +{ + trackContainer::loadSettings( _this ); +} + + + + +void bbEditor::updateAfterTrackAdd( void ) +{ + // make sure, new track(s) have TCOs for every beat/bassline + for( csize i = 0; i < tMax( 1, numOfBBs() ); ++i ) + { + createTCOsForBB( i ); + } +} + + + + +void bbEditor::createTCOsForBB( csize _bb ) +{ + trackVector tv = tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + while( ( *it )->numOfTCOs() < _bb+1 ) + { + midiTime position = midiTime( ( *it )->numOfTCOs(), 0 ); + trackContentObject * tco = ( *it )->addTCO( + ( *it )->createTCO( position ) ); + tco->movePosition( position ); + tco->changeLength( midiTime( 1, 0 ) ); + } + } +} + + + + +void bbEditor::swapBB( csize _bb1, csize _bb2 ) +{ + trackVector tv = tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + ( *it )->swapPositionOfTCOs( _bb1, _bb2 ); + } +} + + + +#include "bb_editor.moc" + diff --git a/src/core/config_mgr.cpp b/src/core/config_mgr.cpp new file mode 100644 index 000000000..9bef358a3 --- /dev/null +++ b/src/core/config_mgr.cpp @@ -0,0 +1,884 @@ +/* + * config_mgr.cpp - implementation of class configManager + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __USE_XOPEN_EXTENDED +#define __USE_XOPEN_EXTENDED +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#endif + + +#include "config_mgr.h" +#include "embed.h" +#include "templates.h" + + + +void mkPath( const QString & _path ) +{ +#ifdef QT4 + // simple clean solution with Qt4... + QDir().mkpath( _path ); +#else + // ...but Qt3 needs additional code... + QDir d( _path ); + vlist dirs; + dirs.push_front( _path ); + QString dir = _path; + while( !QDir( dir ).exists() ) + { + dir = dir.section( '/', 0, -2 ); + dirs.push_front( dir ); + } + while( !dirs.isEmpty() ) + { + d.mkdir( dirs.front() ); + d.setPath( dirs.front() ); + dirs.erase( dirs.begin() ); + } +#endif +} + + + + +void linkFile( const QString & _src, const QString & _dst ) +{ +#ifdef QT4 + // simple clean solution with Qt4... + QFile::link( _src, _dst ); +#else + // ...but Qt3 needs additional (unportable) code... + symlink( _src.ascii(), _dst.ascii() ); +#endif +} + + + + +void copyFile( const QString & _src, const QString & _dst ) +{ +#ifdef QT4 + // simple clean solution with Qt4... + QFile::copy( _src, _dst ); +#else + // ...but Qt3 needs additional code... + QFile in( _src ); + QFile out( _dst ); + in.open( IO_ReadOnly ); + out.open( IO_WriteOnly ); + char buffer[1024]; + while( !in.atEnd() ) + { + Q_LONG read = in.readBlock( buffer, 1024 ); + if( read == -1 ) + { + break; + } + if( out.writeBlock( buffer, read ) == -1 ) + { + break; + } + } +#endif +} + + + + +configManager * configManager::s_instanceOfMe = NULL; + + +configManager::configManager( void ) : + QDialog(), +#ifdef QT4 + m_lmmsRcFile( QDir::home().absolutePath() + "/.lmmsrc.xml" ), + m_lmmsWorkingDir( QDir::home().absolutePath() + "/lmms" ), +#else + m_lmmsRcFile( QDir::home().absPath() + "/.lmmsrc.xml" ), + m_lmmsWorkingDir( QDir::home().absPath() + "/lmms" ), +#endif +#if QT_VERSION >= 0x030200 + m_lmmsDataDir( qApp->applicationDirPath().section( '/', 0, -2 ) + + "/share/lmms/" ), +#else + // hardcode since qt 3.0 doesn't know something like + // applicationDirPath and implmeneting own function would be senseless + // since real support for qt 3.0 is senseless too ;-) + m_lmmsDataDir( "/usr/share/lmms" ), +#endif + m_currentPage( 0 ) +{ +} + + + + +configManager::~configManager() +{ + saveConfigFile(); +} + + + + +void configManager::createWidgets( void ) +{ + m_mainLayout = new QHBoxLayout( this ); + m_mainLayout->setSpacing( 10 ); + m_mainLayout->addSpacing( 8 ); +#ifdef QT4 + setLayout( m_mainLayout ); +#endif + + m_contentWidget = new QWidget( this ); + m_contentLayout = new QVBoxLayout( m_contentWidget ); + m_contentLayout->setSpacing( 10 ); + m_contentLayout->addSpacing( 8 ); +#ifdef QT4 + m_contentWidget->setLayout( m_contentLayout ); +#endif + m_mainLayout->addWidget( m_contentWidget ); + m_mainLayout->addSpacing( 8 ); + + // wizard-init + m_hbar = new QFrame( m_contentWidget ); + m_hbar->setFrameStyle( QFrame::Sunken + QFrame::HLine ); + m_hbar->setFixedHeight( 4 ); + m_title = new QLabel( m_contentWidget ); + m_title->setFixedHeight( 16 ); + QFont f = m_title->font(); + f.setBold( TRUE ); + m_title->setFont( pointSize<12>( f ) ); + + + QWidget * button_widget = new QWidget( m_contentWidget ); + m_buttonLayout = new QHBoxLayout( button_widget ); + m_buttonLayout->addStretch( 1 ); + + m_cancelButton = new QPushButton( tr( "&Cancel" ), button_widget ); + m_backButton = new QPushButton( tr( "< &Back" ), button_widget ); + m_nextButton = new QPushButton( tr( "&Next >" ), button_widget ); + m_finishButton = new QPushButton( tr( "&Finish" ), button_widget ); + + connect( m_cancelButton, SIGNAL( clicked() ), this, SLOT( reject() ) ); + connect( m_backButton, SIGNAL( clicked() ), this, + SLOT( backButtonClicked() ) ); + connect( m_nextButton, SIGNAL( clicked() ), this, + SLOT( nextButtonClicked() ) ); + connect( m_finishButton, SIGNAL( clicked() ), this, + SLOT( accept() ) ); + m_buttonLayout->addWidget( m_cancelButton ); + m_buttonLayout->addWidget( m_backButton ); + m_buttonLayout->addWidget( m_nextButton ); + m_buttonLayout->addWidget( m_finishButton ); + m_buttonLayout->addSpacing( 15 ); + + m_contentLayout->addWidget( m_title ); + m_contentLayout->addWidget( m_hbar ); + m_contentLayout->addWidget( button_widget ); + m_contentLayout->addSpacing( 8 ); + + // wizard-setup + setWindowTitle( tr( "Setup LMMS" ) ); + setWindowIcon( embed::getIconPixmap( "wizard" ) ); + + m_pageIntro = new QWidget( m_contentWidget ); + QHBoxLayout * intro_layout = new QHBoxLayout( m_pageIntro ); + intro_layout->setSpacing( 15 ); + + QLabel * intro_logo_lbl = new QLabel( m_pageIntro ); + intro_logo_lbl->setPixmap( embed::getIconPixmap( "wizard_intro" ) ); + intro_logo_lbl->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + intro_logo_lbl->setFixedSize( 240, 300 ); + + QLabel * intro_txt_lbl = new QLabel( tr( "LMMS needs to be setup in " + "order to run properly. " + "This wizard will help you to " + "setup your personal LMMS-" + "installation.\n\n" + "If you're unsure what to do " + "at a step, just click on " + "'Next'. LMMS will " + "automatically select the best " + "options for you.\n\n\n" + "Now click on 'Next' to get to " + "the next page." ), + m_pageIntro ); +#ifdef QT4 + intro_txt_lbl->setWordWrap( TRUE ); +#else + intro_txt_lbl->setAlignment( intro_txt_lbl->alignment() | WordBreak ); +#endif + + intro_layout->addWidget( intro_logo_lbl ); + intro_layout->addWidget( intro_txt_lbl ); + + + m_pageWorkingDir = new QWidget( m_contentWidget ); + QHBoxLayout * workingdir_layout = new QHBoxLayout( m_pageWorkingDir ); + workingdir_layout->setSpacing( 15 ); + + QLabel * workingdir_logo_lbl = new QLabel( m_pageWorkingDir ); + workingdir_logo_lbl->setPixmap( embed::getIconPixmap( + "wizard_workingdir" ) ); + workingdir_logo_lbl->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + workingdir_logo_lbl->setFixedSize( 240, 300 ); + + QWidget * workingdir_content = new QWidget( m_pageWorkingDir ); + QVBoxLayout * workingdir_content_layout = new QVBoxLayout( + workingdir_content ); + + QLabel * workingdir_txt_lbl = new QLabel( + tr( "When working with LMMS there needs to " + "be a working-directory.\nThis " + "directory is used for storing your " + "projects, presets, samples etc.\n\n\n" + "Please select a directory:" ), + workingdir_content ); +#ifdef QT4 + workingdir_txt_lbl->setWordWrap( TRUE ); +#else + workingdir_txt_lbl->setAlignment( workingdir_txt_lbl->alignment() | + WordBreak ); +#endif + + QWidget * workingdir_input_fields = new QWidget( workingdir_content ); + QHBoxLayout * workingdir_input_fields_layout = new QHBoxLayout( + workingdir_input_fields ); + workingdir_input_fields_layout->setSpacing( 10 ); + + m_wdLineEdit = new QLineEdit( m_lmmsWorkingDir, + workingdir_input_fields ); + connect( m_wdLineEdit, SIGNAL( textChanged( const QString & ) ), this, + SLOT( setWorkingDir( const QString & ) ) ); + + QPushButton * workingdir_select_btn = new QPushButton( + embed::getIconPixmap( "project_open" ), "", + workingdir_input_fields ); + workingdir_select_btn->setFixedSize( 24, 24 ); + connect( workingdir_select_btn, SIGNAL( clicked() ), this, + SLOT( openWorkingDir() ) ); + + workingdir_input_fields_layout->addWidget( m_wdLineEdit ); + workingdir_input_fields_layout->addWidget( workingdir_select_btn ); + + workingdir_content_layout->addWidget( workingdir_txt_lbl ); + workingdir_content_layout->addWidget( workingdir_input_fields ); + + workingdir_layout->addWidget( workingdir_logo_lbl ); + workingdir_layout->addWidget( workingdir_content ); + + + + // page for files-management + m_pageFiles = new QWidget( m_contentWidget ); + QHBoxLayout * files_layout = new QHBoxLayout( m_pageFiles ); + files_layout->setSpacing( 15 ); + + QWidget * files_content = new QWidget( m_pageFiles ); + QVBoxLayout * files_content_layout = new QVBoxLayout( files_content ); + files_content_layout->setSpacing( 10 ); + + QLabel * files_logo_lbl = new QLabel( m_pageFiles ); + files_logo_lbl->setPixmap( embed::getIconPixmap( "wizard_files" ) ); + files_logo_lbl->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + files_logo_lbl->setFixedSize( 240, 300 ); + + QLabel * files_txt_lbl = new QLabel( + tr( "For using the ready presets and samples of " + "LMMS and enjoying the demo-songs the " + "according files have to be copied or " + "linked into your LMMS-working-" + "directory.\nWhen copying files, you " + "can modify them, but they need " + "additional space in your working-" + "directory. If you link files, you " + "cannot modify them, but they need " + "no extra space. So it's recommended " + "to copy presets and demo-projects " + "and link samples, which are bigger " + "in size.\n" ), files_content ); +#ifdef QT4 + files_txt_lbl->setWordWrap( TRUE ); +#else + files_txt_lbl->setAlignment( files_txt_lbl->alignment() | WordBreak ); +#endif + + + QWidget * samples_widget = new QWidget( files_content ); + QHBoxLayout * samples_layout = new QHBoxLayout( samples_widget ); + samples_layout->setSpacing( 5 ); + QLabel * samples_pic_lbl = new QLabel( samples_widget ); + samples_pic_lbl->setPixmap( embed::getIconPixmap( "sound_file" ) ); + QLabel * samples_txt_lbl = new QLabel( tr( "samples:" ), + samples_widget ); + samples_txt_lbl->setFixedWidth( 144 ); + + QButtonGroup * samples_bg = new QButtonGroup( samples_widget ); +#ifndef QT4 + samples_bg->hide(); +#endif + m_samplesCopyRB = new QRadioButton( tr( "copy" ), samples_widget ); + QRadioButton * samples_link_rb = new QRadioButton( tr( "link" ), + samples_widget ); + samples_link_rb->setChecked( TRUE ); + samples_bg->addButton( m_samplesCopyRB ); + samples_bg->addButton( samples_link_rb ); + + samples_layout->addWidget( samples_pic_lbl ); + samples_layout->addWidget( samples_txt_lbl ); + samples_layout->addWidget( m_samplesCopyRB ); + samples_layout->addSpacing( 15 ); + samples_layout->addWidget( samples_link_rb ); + samples_layout->addStretch( 400 ); + + + + QWidget * presets_widget = new QWidget( files_content ); + QHBoxLayout * presets_layout = new QHBoxLayout( presets_widget ); + presets_layout->setSpacing( 5 ); + QLabel * presets_pic_lbl = new QLabel( presets_widget ); + presets_pic_lbl->setPixmap( embed::getIconPixmap( "preset_file" ) ); + QLabel * presets_txt_lbl = new QLabel( tr( "presets:" ), + presets_widget ); + presets_txt_lbl->setFixedWidth( 144 ); + + QButtonGroup * presets_bg = new QButtonGroup( presets_widget ); +#ifndef QT4 + presets_bg->hide(); +#endif + m_presetsCopyRB = new QRadioButton( tr( "copy" ), presets_widget ); + m_presetsCopyRB->setChecked( TRUE ); + QRadioButton * presets_link_rb = new QRadioButton( tr( "link" ), + presets_widget ); + presets_bg->addButton( m_presetsCopyRB ); + presets_bg->addButton( presets_link_rb ); + + presets_layout->addWidget( presets_pic_lbl ); + presets_layout->addWidget( presets_txt_lbl ); + presets_layout->addWidget( m_presetsCopyRB ); + presets_layout->addSpacing( 15 ); + presets_layout->addWidget( presets_link_rb ); + presets_layout->addStretch( 400 ); + + + + QWidget * projects_widget = new QWidget( files_content ); + QHBoxLayout * projects_layout = new QHBoxLayout( projects_widget ); + projects_layout->setSpacing( 5 ); + QLabel * projects_pic_lbl = new QLabel( projects_widget ); + projects_pic_lbl->setPixmap( embed::getIconPixmap( "project_file" ) ); + QLabel * projects_txt_lbl = new QLabel( tr( "demo projects:" ), + projects_widget ); + projects_txt_lbl->setFixedWidth( 144 ); + + QButtonGroup * projects_bg = new QButtonGroup( projects_widget ); +#ifndef QT4 + projects_bg->hide(); +#endif + m_projectsCopyRB = new QRadioButton( tr( "copy" ), projects_widget ); + m_projectsCopyRB->setChecked( TRUE ); + QRadioButton * projects_link_rb = new QRadioButton( tr( "link" ), + projects_widget ); + projects_bg->addButton( m_projectsCopyRB ); + projects_bg->addButton( projects_link_rb ); + + projects_layout->addWidget( projects_pic_lbl ); + projects_layout->addWidget( projects_txt_lbl ); + projects_layout->addWidget( m_projectsCopyRB ); + projects_layout->addSpacing( 15 ); + projects_layout->addWidget( projects_link_rb ); + projects_layout->addStretch( 400 ); + + + + + files_content_layout->addWidget( files_txt_lbl ); + files_content_layout->addWidget( samples_widget ); + files_content_layout->addWidget( presets_widget ); + files_content_layout->addWidget( projects_widget ); + + + files_layout->addWidget( files_logo_lbl ); + files_layout->addWidget( files_content ); + + + + addPage( m_pageIntro, tr( "Welcome to LMMS" ) ); + addPage( m_pageWorkingDir, tr( "Select working directory" ) ); + addPage( m_pageFiles, tr( "Copy or link files" ) ); + switchPage( static_cast( 0 ) ); +} + + + + +void configManager::openWorkingDir( void ) +{ +#ifdef QT4 + QString new_dir = QFileDialog::getExistingDirectory( this, + tr( "Choose LMMS-working-directory" ), + m_lmmsWorkingDir ); +#else + QString new_dir = QFileDialog::getExistingDirectory( m_lmmsWorkingDir, + 0, 0, + tr( "Choose LMMS-working-directory" ), + TRUE ); +#endif + if( new_dir != QString::null ) + { + m_wdLineEdit->setText( new_dir ); + } +} + + + + +void configManager::setWorkingDir( const QString & _wd ) +{ + m_lmmsWorkingDir = _wd; +} + + + + +void configManager::accept( void ) +{ + if( m_lmmsWorkingDir.right( 1 ) != "/" ) + { + m_lmmsWorkingDir += '/'; + } + if( !QDir( m_lmmsWorkingDir ).exists() ) + { + if( QMessageBox:: +#if QT_VERSION >= 0x030200 + question +#else + information +#endif + ( 0, tr( "Directory not existing" ), + tr( "The directory you " + "specified does not " + "exist. Create it?" ), + QMessageBox::Yes, QMessageBox::No ) + == QMessageBox::Yes ) + { + mkPath( m_lmmsWorkingDir ); + } + else + { + switchPage( m_pageWorkingDir ); + return; + } + } + processFilesRecursively( m_lmmsDataDir + "samples/", m_lmmsWorkingDir + + "samples/", + m_samplesCopyRB->isChecked() ? + ©File : + &linkFile ); + processFilesRecursively( m_lmmsDataDir + "presets/", m_lmmsWorkingDir + + "presets/", + m_presetsCopyRB->isChecked() ? + ©File : + &linkFile ); + processFilesRecursively( m_lmmsDataDir + "projects/", m_lmmsWorkingDir + + "projects/", + m_projectsCopyRB->isChecked() ? + ©File : + &linkFile ); + setValue( "paths", "workingdir", m_lmmsWorkingDir ); + + saveConfigFile(); + + QDialog::accept(); +} + + + + +void configManager::backButtonClicked( void ) +{ + switchPage( m_currentPage-1 ); +} + + + + +void configManager::nextButtonClicked( void ) +{ + switchPage( m_currentPage+1 ); +} + + + + +void configManager::switchPage( csize _pg ) +{ + if( m_currentPage >= 0 && m_currentPage < m_pages.size() ) + { + m_pages[m_currentPage].first->hide(); +#ifdef QT4 + m_contentLayout->removeWidget( m_pages[m_currentPage].first ); +#else +#if QT_VERSION >= 0x030100 + m_contentLayout->remove( m_pages[m_currentPage].first ); +#else + m_pages[m_currentPage].first->hide(); +#endif +#endif + } + if( _pg < m_pages.size() ) + { + QWidget * p = m_pages[_pg].first; + m_contentLayout->insertWidget( 2, p ); + p->show(); + p->setFocus(); + m_title->setText( m_pages[_pg].second ); + m_currentPage = _pg; + } + m_backButton->setEnabled( _pg > 0 ); + if( _pg == m_pages.size() - 1 ) + { + m_nextButton->setEnabled( FALSE ); + m_finishButton->setEnabled( TRUE ); + m_finishButton->setDefault( TRUE ); + } + else + { + m_nextButton->setEnabled( TRUE ); + m_nextButton->setDefault( TRUE ); + m_finishButton->setEnabled( FALSE ); + } +} + + + + +void configManager::switchPage( QWidget * _pg ) +{ + for( csize i = 0; i < m_pages.size(); ++i ) + { + if( m_pages[i].first == _pg ) + { + switchPage( i ); + break; + } + } +} + + + + +void configManager::addPage( QWidget * _w, const QString & _title ) +{ + _w->hide(); + m_pages.push_back( qMakePair( _w, _title ) ); +} + + + + +const QString & configManager::value( const QString & _class, + const QString & _attribute ) const +{ + if( m_settings.contains( _class ) ) + { + for( stringPairVector::const_iterator it = + m_settings[_class].begin(); + it != m_settings[_class].end(); ++it ) + { + if( ( *it ).first == _attribute ) + { + return( ( *it ).second ); + } + } + } + static QString empty; + return( empty ); +} + + + + +void configManager::setValue( const QString & _class, + const QString & _attribute, + const QString & _value ) +{ + if( m_settings.contains( _class ) ) + { + for( stringPairVector::iterator it = m_settings[_class].begin(); + it != m_settings[_class].end(); ++it ) + { + if( ( *it ).first == _attribute ) + { + ( *it ).second = _value; + return; + } + } + } + // not in map yet, so we have to add it... + m_settings[_class].push_back( qMakePair( _attribute, _value ) ); +} + + + + +bool configManager::loadConfigFile( void ) +{ + createWidgets(); + + // read the XML file and create DOM tree + QFile cfg_file( m_lmmsRcFile ); +#ifdef QT4 + if( !cfg_file.open( QIODevice::ReadOnly ) ) +#else + if( !cfg_file.open( IO_ReadOnly ) ) +#endif + { +#ifdef QT4 + if( !( exec() && cfg_file.open( QIODevice::ReadOnly ) ) ) +#else + if( !( exec() && cfg_file.open( IO_ReadOnly ) ) ) +#endif + { + return( FALSE ); + } + } + QDomDocument dom_tree; + if( !dom_tree.setContent( &cfg_file ) ) + { + QMessageBox::critical( 0, tr( "Error in configuration-file" ), + tr( "Error while parsing " + "configuration-file %1.\n" + "The setup-wizard will be " + "shown for reconfiguring LMMS." + ).arg( m_lmmsRcFile ) ); + cfg_file.close(); + if( exec() ) + { + return( loadConfigFile() ); + } + else + { + return( FALSE ); + } + } + cfg_file.close(); + + + // get the head information from the DOM + QDomElement root = dom_tree.documentElement(); + if( root.isElement() ) + { + QString cfg_file_ver = root.toElement().attribute( "version" ); + if( ( cfg_file_ver.length() == 0 || cfg_file_ver != VERSION ) && + QMessageBox:: +#if QT_VERSION >= 0x030200 + question +#else + information +#endif + ( 0, tr( "Version mismatches" ), + tr( "Accordingly to the information in " + "your LMMS-configuration-file " + "you seem\nto have run a " + "different (probably older) " + "version of LMMS before.\n" + "It is recommended to run the " + "setup-wizard again to ensure " + "that\nthe latest samples, " + "presets, demo-projects etc. " + "are installed in your\n" + "LMMS-working-directory. " + "Run the setup-wizard now?" ), + QMessageBox::Yes, QMessageBox::No ) + == QMessageBox::Yes ) + { + if( exec() ) + { + return( loadConfigFile() ); + } + } + } + + QDomNode node = root.firstChild(); + + // create the settings-map out of the DOM + while( !node.isNull() ) + { + if( node.isElement() && node.toElement().hasAttributes () ) + { + stringPairVector attr; + QDomNamedNodeMap node_attr = + node.toElement().attributes(); + for( unsigned int i = 0; i < node_attr.count(); ++i ) + { + QDomNode n = node_attr.item( i ); + if( n.isAttr() ) + { + attr.push_back( qMakePair( + n.toAttr().name(), + n.toAttr().value() ) ); + } + } + m_settings[node.nodeName()] = attr; + } + node = node.nextSibling(); + } + + m_lmmsWorkingDir = value( "paths", "workingdir" ); + + return( TRUE ); +} + + + + +void configManager::saveConfigFile( void ) +{ + QDomDocument doc( "lmms-config-file" ); + + QDomElement lmms_config = doc.createElement( "lmms" ); + lmms_config.setAttribute( "version", VERSION ); + doc.appendChild( lmms_config ); + + for( settingsMap::iterator it = m_settings.begin(); + it != m_settings.end(); ++it ) + { + QDomElement n = doc.createElement( it.key() ); + for( stringPairVector::iterator it2 = ( *it ).begin(); + it2 != ( *it ).end(); ++it2 ) + { + n.setAttribute( ( *it2 ).first, ( *it2 ).second ); + } + lmms_config.appendChild( n ); + } + +#if QT_VERSION >= 0x030100 + QString xml = "\n" + doc.toString( 2 ); +#else + QString xml = "\n" + doc.toString(); +#endif + + QFile outfile( m_lmmsRcFile ); +#ifdef QT4 + if( !outfile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) +#else + if( !outfile.open( IO_WriteOnly | IO_Truncate ) ) +#endif + { + QMessageBox::critical( NULL, tr( "Could not save config-file" ), + tr( "Could not save configuration " + "file %1. You probably are not " + "permitted to write to this " + "file.\n" + "Please make sure you have " + "write-access to the file and " + "try again." ).arg( + m_lmmsRcFile ) ); + return; + } + +#ifdef QT4 + outfile.write( xml.toAscii().constData(), xml.length() ); +#else + outfile.writeBlock( xml.ascii(), xml.length() ); +#endif + outfile.close(); +} + + + + +void configManager::processFilesRecursively( const QString & _src_dir, + const QString & _dst_dir, + void( * _proc_func )( const QString & _src, const QString & _dst ) ) +{ + mkPath( _dst_dir ); + QStringList files = QDir( _src_dir ).entryList(); + for( QStringList::iterator it = files.begin(); it != files.end(); ++it ) + { + if( ( *it )[0] == '.' ) + { + continue; + } + if( QFileInfo( _src_dir + *it ).isFile() ) + { + _proc_func( _src_dir + *it, _dst_dir + *it ); + } + else if( QFileInfo( _src_dir + *it ).isDir () ) + { + processFilesRecursively( _src_dir + *it + "/", + _dst_dir + *it + "/", + _proc_func ); + } + } +} + + + + +#include "config_mgr.moc" + diff --git a/src/core/envelope_and_lfo_widget.cpp b/src/core/envelope_and_lfo_widget.cpp new file mode 100644 index 000000000..12d1790fd --- /dev/null +++ b/src/core/envelope_and_lfo_widget.cpp @@ -0,0 +1,1118 @@ +/* + * envelope_widget.cpp - widget which is m_used by envelope/lfo/filter-tab of + * channel-window + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include + +#define isChecked isOn +#define setChecked setOn + +#endif + + +#include "envelope_and_lfo_widget.h" +#include "song_editor.h" +#include "embed.h" +#include "knob.h" +#include "pixmap_button.h" +#include "oscillator.h" +#include "debug.h" +#include "tooltip.h" + + +// how long should be each envelope-segment maximal (e.g. attack)? +const float SECS_PER_ENV_SEGMENT = 5.0f; +// how long should be one LFO-oscillation maximal? +const float SECS_PER_LFO_OSCILLATION = 20.0f; + + +const int env_graph_x = 8; +const int env_graph_y = 8; + +const int env_knobs_y = 43; +const int env_knobs_lbl_y = env_knobs_y+35; +const int knob_x_spacing = 32; +const int predelay_knob_x = 6; +const int attack_knob_x = predelay_knob_x+knob_x_spacing; +const int hold_knob_x = attack_knob_x+knob_x_spacing; +const int decay_knob_x = hold_knob_x+knob_x_spacing; +const int sustain_knob_x = decay_knob_x+knob_x_spacing; +const int release_knob_x = sustain_knob_x+knob_x_spacing; +const int amount_knob_x = release_knob_x+knob_x_spacing; + +const float time_unit_width = 36.0; + + +const int lfo_graph_x = 8; +const int lfo_graph_y = env_knobs_lbl_y+14; +const int lfo_knob_y = lfo_graph_y-2; +const int lfo_knobs_lbl_y = lfo_knob_y+35; +const int lfo_predelay_knob_x = lfo_graph_x + 100; +const int lfo_attack_knob_x = lfo_predelay_knob_x+knob_x_spacing; +const int lfo_speed_knob_x = lfo_attack_knob_x+knob_x_spacing; +const int lfo_amount_knob_x = lfo_speed_knob_x+knob_x_spacing; +const int lfo_shapes_x = lfo_graph_x;//predelay_knob_x; +const int lfo_shapes_y = lfo_graph_y + 50; + + +QPixmap * envelopeAndLFOWidget::s_envGraph = NULL; +QPixmap * envelopeAndLFOWidget::s_lfoGraph = NULL; +Uint32 envelopeAndLFOWidget::s_lfoFrame = 0; + + + +envelopeAndLFOWidget::envelopeAndLFOWidget( float _value_for_zero_amount, + QWidget * _parent ) : + QWidget( _parent ), + settings(), +#ifdef QT4 + specialBgHandlingWidget( palette().color( backgroundRole() ) ), +#else + specialBgHandlingWidget( paletteBackgroundColor() ), +#endif + m_used( FALSE ), + m_valueForZeroAmount( _value_for_zero_amount ), + m_pahdEnv( NULL ), + m_rEnv( NULL ), + m_lfoAmountIsZero( FALSE ), + m_lfoShapeData( NULL ), + m_lfoShape( SIN ), + m_busy( FALSE ) +{ + if( s_envGraph == NULL ) + { + s_envGraph = new QPixmap( embed::getIconPixmap( + "envelope_graph" ) ); + } + if( s_lfoGraph == NULL ) + { + s_lfoGraph = new QPixmap( embed::getIconPixmap( "lfo_graph" ) ); + } + + m_predelayKnob = new knob( knobBright_26, this, tr( "Predelay-time" ) ); + m_predelayKnob->setLabel( tr( "DEL" ) ); + m_predelayKnob->setRange( 0.0, 1.0, 0.001 ); + m_predelayKnob->setValue( 0.0, TRUE ); + m_predelayKnob->move( predelay_knob_x, env_knobs_y ); + m_predelayKnob->setHintText( tr( "Predelay:" ) + " ", "" ); +#ifdef QT4 + m_predelayKnob->setWhatsThis( +#else + QWhatsThis::add( m_predelayKnob, +#endif + tr( "Use this knob for setting predelay of the current " + "envelope. The bigger this value the longer the time " + "before start of actual envelope." ) ); + connect( m_predelayKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_attackKnob = new knob( knobBright_26, this, tr( "Attack-time" ) ); + m_attackKnob->setLabel( tr( "ATT" ) ); + m_attackKnob->setRange( 0.0, 1.0, 0.001 ); + m_attackKnob->setValue( 0.0, TRUE ); + m_attackKnob->move( attack_knob_x, env_knobs_y ); + m_attackKnob->setHintText( tr( "Attack:" )+" ", "" ); +#ifdef QT4 + m_attackKnob->setWhatsThis( +#else + QWhatsThis::add( m_attackKnob, +#endif + tr( "Use this knob for setting attack-time of the current " + "envelope. The bigger this value the longer the " + "envelope needs to increase to attack-level. " + "Choose a small value for instruments like pianos " + "and a big value for strings." ) ); + connect( m_attackKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_holdKnob = new knob( knobBright_26, this, tr( "Hold-time" ) ); + m_holdKnob->setLabel( tr( "HOLD" ) ); + m_holdKnob->setRange( 0.0, 1.0, 0.001 ); + m_holdKnob->setValue( 0.5, TRUE ); + m_holdKnob->move( hold_knob_x, env_knobs_y ); + m_holdKnob->setHintText( tr( "Hold:" ) + " ", "" ); +#ifdef QT4 + m_holdKnob->setWhatsThis( +#else + QWhatsThis::add( m_holdKnob, +#endif + tr( "Use this knob for setting hold-time of the current " + "envelope. The bigger this value the longer the " + "envelope holds attack-level before it begins to " + "decrease to sustain-level." ) ); + connect( m_holdKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_decayKnob = new knob( knobBright_26, this, tr( "Decay-time" ) ); + m_decayKnob->setLabel( tr( "DEC" ) ); + m_decayKnob->setRange( 0.0, 1.0, 0.001 ); + m_decayKnob->setValue( 0.5, TRUE ); + m_decayKnob->move( decay_knob_x, env_knobs_y ); + m_decayKnob->setHintText( tr( "Decay:" ) + " ", "" ); +#ifdef QT4 + m_decayKnob->setWhatsThis( +#else + QWhatsThis::add( m_decayKnob, +#endif + tr( "Use this knob for setting decay-time of the current " + "envelope. The bigger this value the longer the " + "envelope needs to decrease from attack-level to " + "sustain-level. Choose a small value for instruments " + "like pianos." ) ); + connect( m_decayKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_sustainKnob = new knob( knobBright_26, this, tr( "Sustain-level" ) ); + m_sustainKnob->setLabel( tr( "SUST" ) ); + m_sustainKnob->setRange( 0.0, 1.0, 0.001 ); + m_sustainKnob->setValue( 0.5, TRUE ); + m_sustainKnob->move( sustain_knob_x, env_knobs_y ); + m_sustainKnob->setHintText( tr( "Sustain:" ) + " ", "" ); +#ifdef QT4 + m_sustainKnob->setWhatsThis( +#else + QWhatsThis::add( m_sustainKnob, +#endif + tr( "Use this knob for setting sustain-level of the current " + "envelope. The bigger this value the higher the level " + "on which the envelope stays before going down to " + "zero." ) ); + connect( m_sustainKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_releaseKnob = new knob( knobBright_26, this, tr( "Release-time" ) ); + m_releaseKnob->setLabel( tr( "REL" ) ); + m_releaseKnob->setRange( 0.0, 1.0, 0.001 ); + m_releaseKnob->setValue( 0.1, TRUE ); + m_releaseKnob->move( release_knob_x, env_knobs_y ); + m_releaseKnob->setHintText( tr( "Release:" ) + " ", "" ); +#ifdef QT4 + m_releaseKnob->setWhatsThis( +#else + QWhatsThis::add( m_releaseKnob, +#endif + tr( "Use this knob for setting release-time of the current " + "envelope. The bigger this value the longer the " + "envelope needs to decrease from sustain-level to " + "zero. Choose a big value for soft instruments like " + "strings." ) ); + connect( m_releaseKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_amountKnob = new knob( knobBright_26, this, + tr( "Modulation amount" ) ); + m_amountKnob->setLabel( tr( "AMT" ) ); + m_amountKnob->setRange( -1.0, 1.0, 0.005 ); + m_amountKnob->setValue( 0.0, TRUE ); + m_amountKnob->move( amount_knob_x, env_graph_y ); + m_amountKnob->setHintText( tr( "Modulation amount:" ) + " ", "" ); +#ifdef QT4 + m_amountKnob->setWhatsThis( +#else + QWhatsThis::add( m_amountKnob, +#endif + tr( "Use this knob for setting modulation amount of the " + "current envelope. The bigger this value the more the " + "according size (e.g. volume or cutoff-frequency) " + "will be influenced by this envelope." ) ); + connect( m_amountKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + + + m_lfoPredelayKnob = new knob( knobBright_26, this, + tr( "LFO-predelay-time" ) ); + m_lfoPredelayKnob->setLabel( tr( "DEL" ) ); + m_lfoPredelayKnob->setRange( 0.0, 1.0, 0.001 ); + m_lfoPredelayKnob->setValue( 0.0, TRUE ); + m_lfoPredelayKnob->move( lfo_predelay_knob_x, lfo_knob_y ); + m_lfoPredelayKnob->setHintText( tr( "LFO-predelay:" ) + " ", "" ); +#ifdef QT4 + m_lfoPredelayKnob->setWhatsThis( +#else + QWhatsThis::add( m_lfoPredelayKnob, +#endif + tr( "Use this knob for setting predelay-time of the current " + "LFO. The bigger this value the the time until the " + "LFO starts to oscillate." ) ); + connect( m_lfoPredelayKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_lfoAttackKnob = new knob( knobBright_26, this, + tr( "LFO-attack-time" ) ); + m_lfoAttackKnob->setLabel( tr( "ATT" ) ); + m_lfoAttackKnob->setRange( 0.0, 1.0, 0.001 ); + m_lfoAttackKnob->setValue( 0.0, TRUE ); + m_lfoAttackKnob->move( lfo_attack_knob_x, lfo_knob_y ); + m_lfoAttackKnob->setHintText( tr( "LFO-attack:" ) + " ", "" ); +#ifdef QT4 + m_lfoAttackKnob->setWhatsThis( +#else + QWhatsThis::add( m_lfoAttackKnob, +#endif + tr( "Use this knob for setting attack-time of the current LFO. " + "The bigger this value the longer the LFO needs to " + "increase its amplitude to maximum." ) ); + connect( m_lfoAttackKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_lfoSpeedKnob = new knob( knobBright_26, this, tr( "LFO-speed" ) ); + m_lfoSpeedKnob->setLabel( tr( "SPD" ) ); + m_lfoSpeedKnob->setRange( 0.01, 1.0, 0.0001 ); + m_lfoSpeedKnob->setValue( 0.1, TRUE ); + m_lfoSpeedKnob->move( lfo_speed_knob_x, lfo_knob_y ); + m_lfoSpeedKnob->setHintText( tr( "LFO-speed:" ) + " ", "" ); +#ifdef QT4 + m_lfoSpeedKnob->setWhatsThis( +#else + QWhatsThis::add( m_lfoSpeedKnob, +#endif + tr( "Use this knob for setting speed of the current LFO. The " + "bigger this value the faster the LFO oscillates and " + "the faster will be your effect." ) ); + connect( m_lfoSpeedKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + m_lfoAmountKnob = new knob( knobBright_26, this, + tr( "LFO-modulation-amount" ) ); + m_lfoAmountKnob->setLabel( tr( "AMT" ) ); + m_lfoAmountKnob->setRange( -1.0, 1.0, 0.005 ); + m_lfoAmountKnob->setValue( 0.0, TRUE ); + m_lfoAmountKnob->move( lfo_amount_knob_x, lfo_knob_y ); + m_lfoAmountKnob->setHintText( tr( "Modulation amount:" ) + " ", "" ); +#ifdef QT4 + m_lfoAmountKnob->setWhatsThis( +#else + QWhatsThis::add( m_lfoAmountKnob, +#endif + tr( "Use this knob for setting modulation amount of the " + "current LFO. The bigger this value the more the " + "selected size (e.g. volume or cutoff-frequency) will " + "be influenced by this LFO." ) ); + connect( m_lfoAmountKnob, SIGNAL( valueChanged( float ) ), this, + SLOT( updateAfterKnobChange( float ) ) ); + + + m_sinLfoBtn = new pixmapButton( this ); + m_sinLfoBtn->move( lfo_shapes_x, lfo_shapes_y ); + m_sinLfoBtn->setActiveGraphic( embed::getIconPixmap( + "sin_wave_active" ) ); + m_sinLfoBtn->setInactiveGraphic( embed::getIconPixmap( + "sin_wave_inactive" ) ); + m_sinLfoBtn->setChecked( TRUE ); +#ifdef QT4 + m_sinLfoBtn->setWhatsThis( +#else + QWhatsThis::add( m_sinLfoBtn, +#endif + tr( "Click here if you want a sine-wave for current " + "oscillator." ) ); + + m_triangleLfoBtn = new pixmapButton( this ); + m_triangleLfoBtn->move( lfo_shapes_x+15, lfo_shapes_y ); + m_triangleLfoBtn->setActiveGraphic( embed::getIconPixmap( + "triangle_wave_active" ) ); + m_triangleLfoBtn->setInactiveGraphic( embed::getIconPixmap( + "triangle_wave_inactive" ) ); +#ifdef QT4 + m_triangleLfoBtn->setWhatsThis( +#else + QWhatsThis::add( m_triangleLfoBtn, +#endif + tr( "Click here if you want a triangle-wave for current " + "oscillator." ) ); + + m_sawLfoBtn = new pixmapButton( this ); + m_sawLfoBtn->move( lfo_shapes_x+30, lfo_shapes_y ); + m_sawLfoBtn->setActiveGraphic( embed::getIconPixmap( + "saw_wave_active" ) ); + m_sawLfoBtn->setInactiveGraphic( embed::getIconPixmap( + "saw_wave_inactive" ) ); +#ifdef QT4 + m_sawLfoBtn->setWhatsThis( +#else + QWhatsThis::add( m_sawLfoBtn, +#endif + tr( "Click here if you want a saw-wave for current " + "oscillator." ) ); + + m_sqrLfoBtn = new pixmapButton( this ); + m_sqrLfoBtn->move( lfo_shapes_x+45, lfo_shapes_y ); + m_sqrLfoBtn->setActiveGraphic( embed::getIconPixmap( + "square_wave_active" ) ); + m_sqrLfoBtn->setInactiveGraphic( embed::getIconPixmap( + "square_wave_inactive" ) ); +#ifdef QT4 + m_sqrLfoBtn->setWhatsThis( +#else + QWhatsThis::add( m_sqrLfoBtn, +#endif + tr( "Click here if you want a square-wave for current " + "oscillator." ) ); + + connect( m_sinLfoBtn, SIGNAL( toggled( bool ) ), this, + SLOT( lfoSinWaveCh( bool ) ) ); + connect( m_triangleLfoBtn, SIGNAL( toggled( bool ) ), this, + SLOT( lfoTriangleWaveCh( bool ) ) ); + connect( m_sawLfoBtn, SIGNAL( toggled( bool ) ), this, + SLOT( lfoSawWaveCh( bool ) ) ); + connect( m_sqrLfoBtn, SIGNAL( toggled( bool ) ), this, + SLOT( lfoSquareWaveCh( bool ) ) ); + + QButtonGroup * lfo_shapes_algo_group = new QButtonGroup( this ); + lfo_shapes_algo_group->addButton( m_sinLfoBtn ); + lfo_shapes_algo_group->addButton( m_triangleLfoBtn ); + lfo_shapes_algo_group->addButton( m_sawLfoBtn ); + lfo_shapes_algo_group->addButton( m_sqrLfoBtn ); + lfo_shapes_algo_group->setExclusive( TRUE ); +#ifndef QT4 + lfo_shapes_algo_group->hide(); +#endif + + m_x100Btn = new pixmapButton( this ); + m_x100Btn->move( lfo_predelay_knob_x, lfo_graph_y + 36 ); + m_x100Btn->setBgGraphic( specialBgHandlingWidget::getBackground( + m_x100Btn ) ); + +/* m_x100Btn->setActiveGraphic( embed::getIconPixmap( "x100_active" ) ); + m_x100Btn->setInactiveGraphic( embed::getIconPixmap( + "x100_inactive" ) );*/ + m_x100Btn->setBgGraphic( + specialBgHandlingWidget::getBackground( m_x100Btn ) ); +#ifdef QT4 + m_x100Btn->setWhatsThis( +#else + QWhatsThis::add( m_x100Btn, +#endif + tr( "Click here if the frequency of this LFO should be " + "multiplied with 100." ) ); + toolTip::add( m_x100Btn, tr( "multiply LFO-frequency with 100" ) ); + connect( m_x100Btn, SIGNAL( toggled( bool ) ), this, + SLOT( x100Toggled( bool ) ) ); + + QLabel * x100_lbl = new QLabel( tr( "FREQ x 100" ), this ); + x100_lbl->setFont( pointSize<6>( x100_lbl->font() ) ); + x100_lbl->move( m_x100Btn->x() + 16, m_x100Btn->y() ); + x100_lbl->setFixedHeight( 10 ); + + m_controlEnvAmountBtn = new pixmapButton( this ); + m_controlEnvAmountBtn->move( lfo_predelay_knob_x, lfo_graph_y + 54 ); + m_controlEnvAmountBtn->setBgGraphic( + specialBgHandlingWidget::getBackground( + m_controlEnvAmountBtn ) ); +/* m_controlEnvAmountBtn->setActiveGraphic( embed::getIconPixmap( + "control_env_amount_active" ) ); + m_controlEnvAmountBtn->setInactiveGraphic( embed::getIconPixmap( + "control_env_amount_inactive" ) );*/ +#ifdef QT4 + m_controlEnvAmountBtn ->setWhatsThis( +#else + QWhatsThis::add( m_controlEnvAmountBtn, +#endif + tr( "Click here to make the envelope-amount controlled by this " + "LFO." ) ); + QLabel * cea_lbl = new QLabel( tr( "MODULATE ENV-AMOUNT" ), this ); + cea_lbl->setFont( pointSize<6>( cea_lbl->font() ) ); + cea_lbl->move( m_controlEnvAmountBtn->x() + 16, + m_controlEnvAmountBtn->y() ); + cea_lbl->setFixedSize( 110, 10 ); + toolTip::add( m_controlEnvAmountBtn, + tr( "control envelope-amount by this LFO" ) ); + + +#ifndef QT4 + // set background-mode for flicker-free redraw + setBackgroundMode( Qt::NoBackground ); +#endif + + connect( mixer::inst(), SIGNAL( sampleRateChanged() ), this, + SLOT( updateSampleVars() ) ); + + updateSampleVars(); +} + + + + +envelopeAndLFOWidget::~envelopeAndLFOWidget() +{ + delete[] m_pahdEnv; + delete[] m_rEnv; + delete[] m_lfoShapeData; +} + + + + +void envelopeAndLFOWidget::triggerLFO( void ) +{ + s_lfoFrame += mixer::inst()->framesPerAudioBuffer(); +} + + + + +void envelopeAndLFOWidget::resetLFO( void ) +{ + s_lfoFrame = 0; +} + + + + +inline float FASTCALL envelopeAndLFOWidget::lfoLevel( Uint32 _frame, + Uint32 _frame_offset ) const +{ + if( m_lfoAmountIsZero == FALSE && _frame > m_lfoPredelayFrames ) + { +#ifdef LMMS_DEBUG + assert( m_lfoShapeData != NULL ); +#endif + _frame -= m_lfoPredelayFrames; + if( _frame > m_lfoAttackFrames ) + { + return( m_lfoShapeData[( s_lfoFrame + _frame_offset ) % + m_lfoOscillationFrames] ); + } + return( m_lfoShapeData[( s_lfoFrame + _frame_offset ) % + m_lfoOscillationFrames] * + _frame / m_lfoAttackFrames ); + } + return( 0.0f ); +} + + + + +float FASTCALL envelopeAndLFOWidget::level( Uint32 _frame, + Uint32 _release_begin, + Uint32 _frame_offset ) const +{ + if( m_busy ) + { + return( 0.0f ); + } + if( _frame < m_pahdFrames ) + { + if( m_controlEnvAmountBtn->isChecked() ) + { + return( m_pahdEnv[_frame] * ( 0.5f + + lfoLevel( _frame, _frame_offset ) ) ); + } + else + { + return( m_pahdEnv[_frame] + + lfoLevel( _frame, _frame_offset ) ); + } + } + else if( _frame > _release_begin ) + { + _frame -= _release_begin; + if( _frame < m_rFrames ) + { + if( m_controlEnvAmountBtn->isChecked() ) + { + return( m_rEnv[_frame] * ( 0.5f + + lfoLevel( _frame, _frame_offset ) ) ); + } + else + { + return( m_rEnv[_frame] + + lfoLevel( _frame, _frame_offset ) ); + } + } + else + { + return( 0.0f ); + } + } + if( m_controlEnvAmountBtn->isChecked() ) + { + return( m_sustainLevel * ( 0.5f + + lfoLevel( _frame, _frame_offset ) ) ); + } + return( m_sustainLevel + lfoLevel( _frame, _frame_offset ) ); +} + + + + +void envelopeAndLFOWidget::saveSettings( QDomDocument & , + QDomElement & _parent ) +{ + _parent.setAttribute( "pdel", QString::number( + m_predelayKnob->value() ) ); + _parent.setAttribute( "att", QString::number( m_attackKnob->value() ) ); + _parent.setAttribute( "hold", QString::number( m_holdKnob->value() ) ); + _parent.setAttribute( "dec", QString::number( m_decayKnob->value() ) ); + _parent.setAttribute( "sus", QString::number( + m_sustainKnob->value() ) ); + _parent.setAttribute( "rel", QString::number( + m_releaseKnob->value() ) ); + _parent.setAttribute( "amt", QString::number( m_amountKnob->value() ) ); + _parent.setAttribute( "lshp", QString::number( m_lfoShape ) ); + _parent.setAttribute( "lpdel", QString::number( + m_lfoPredelayKnob->value() ) ); + _parent.setAttribute( "latt", QString::number( + m_lfoAttackKnob->value() ) ); + _parent.setAttribute( "lspd", QString::number( + m_lfoSpeedKnob->value() ) ); + _parent.setAttribute( "lamt", QString::number( + m_lfoAmountKnob->value() ) ); + _parent.setAttribute( "x100", QString::number( + m_x100Btn->isChecked() ) ); + _parent.setAttribute( "ctlenvamt", QString::number( + m_controlEnvAmountBtn->isChecked() ) ); +} + + + + +void envelopeAndLFOWidget::loadSettings( const QDomElement & _this ) +{ + m_busy = TRUE; + + m_predelayKnob->setValue( _this.attribute( "pdel" ).toFloat() ); + m_attackKnob->setValue( _this.attribute( "att" ).toFloat() ); + m_holdKnob->setValue( _this.attribute( "hold" ).toFloat() ); + m_decayKnob->setValue( _this.attribute( "dec" ).toFloat() ); + m_sustainKnob->setValue( _this.attribute( "sus" ).toFloat() ); + m_releaseKnob->setValue( _this.attribute( "rel" ).toFloat() ); + m_amountKnob->setValue( _this.attribute( "amt" ).toFloat() ); + m_lfoShape = static_cast( _this.attribute( + "lshp" ).toInt() ); + m_lfoPredelayKnob->setValue( _this.attribute( "lpdel" ).toFloat() ); + m_lfoAttackKnob->setValue( _this.attribute( "latt" ).toFloat() ); + m_lfoSpeedKnob->setValue( _this.attribute( "lspd" ).toFloat() ); + m_lfoAmountKnob->setValue( _this.attribute( "lamt" ).toFloat() ); + m_x100Btn->setChecked( _this.attribute( "x100" ).toInt() ); + m_controlEnvAmountBtn->setChecked( _this.attribute( + "ctlenvamt" ).toInt() ); + + switch( m_lfoShape ) + { + case SIN: + m_sinLfoBtn->setChecked( TRUE ); + break; + + case TRIANGLE: + m_triangleLfoBtn->setChecked( TRUE ); + break; + + case SAW: + m_sawLfoBtn->setChecked( TRUE ); + break; + + case SQUARE: + m_sqrLfoBtn->setChecked( TRUE ); + break; + } + + m_busy = FALSE; + + updateSampleVars(); + update(); +} + + + + + +void envelopeAndLFOWidget::paintEvent( QPaintEvent * ) +{ + updateSampleVars(); + +#ifdef QT4 + QPainter p( this ); + p.setRenderHint( QPainter::Antialiasing ); +#else + // create pixmap for whole widget + QPixmap pm( size() ); + pm.fill( this, rect().topLeft() ); + + // and a painter for it + QPainter p( &pm, this ); +#endif + + // set smaller font + p.setFont( pointSize<6>( p.font() ) ); + + // draw envelope-graph + p.drawPixmap( env_graph_x, env_graph_y, *s_envGraph ); + // draw LFO-graph + p.drawPixmap( lfo_graph_x, lfo_graph_y, *s_lfoGraph ); + + + p.setFont( pointSize<8>( p.font() ) ); + + const float gray_amount = 1.0f - fabsf( m_amountKnob->value() ); + + p.setPen( QPen( QColor( static_cast( 96 * gray_amount ), + static_cast( 255 - 159 * gray_amount ), + static_cast( 128 - 32 * gray_amount ) ), + 2 ) ); + + const QColor end_points_color( 0xFF, 0xBF, 0x22 ); + const QColor end_points_bg_color( 0, 0, 2 ); + + const int y_base = env_graph_y + s_envGraph->height() - 3; + const int avail_height = s_envGraph->height() - 6; + + int x1 = env_graph_x + 2 + static_cast( m_predelayKnob->value() * + time_unit_width ); + int x2 = x1 + static_cast( m_attackKnob->value() * + time_unit_width ); + + p.drawLine( x1, y_base, x2, y_base - avail_height ); + p.fillRect( x1 - 1, y_base - 2, 4, 4, end_points_bg_color ); + p.fillRect( x1, y_base - 1, 2, 2, end_points_color ); + x1 = x2; + x2 = x1 + static_cast( m_holdKnob->value() * time_unit_width ); + + p.drawLine( x1, y_base - avail_height, x2, y_base - avail_height ); + p.fillRect( x1 - 1, y_base - 2 - avail_height, 4, 4, + end_points_bg_color ); + p.fillRect( x1, y_base-1-avail_height, 2, 2, end_points_color ); + x1 = x2; + x2 = x1 + static_cast( ( m_decayKnob->value() * + m_sustainKnob->value() ) * + time_unit_width ); + + p.drawLine( x1, y_base-avail_height, x2, static_cast( y_base - + avail_height + + m_sustainKnob->value() * avail_height ) ); + p.fillRect( x1 - 1, y_base - 2 - avail_height, 4, 4, + end_points_bg_color ); + p.fillRect( x1, y_base - 1 - avail_height, 2, 2, end_points_color ); + x1 = x2; + x2 = x1 + static_cast( m_releaseKnob->value() * time_unit_width ); + + p.drawLine( x1, static_cast( y_base - avail_height + + m_sustainKnob->value() * + avail_height ), x2, y_base ); + p.fillRect( x1-1, static_cast( y_base - avail_height + + m_sustainKnob->value() * + avail_height ) - 2, 4, 4, + end_points_bg_color ); + p.fillRect( x1, static_cast( y_base - avail_height + + m_sustainKnob->value() * + avail_height ) - 1, 2, 2, + end_points_color ); + p.fillRect( x2 - 1, y_base - 2, 4, 4, end_points_bg_color ); + p.fillRect( x2, y_base - 1, 2, 2, end_points_color ); + + + int lfo_graph_w = s_lfoGraph->width() - 6; // substract border + int lfo_graph_h = s_lfoGraph->height() - 6; // substract border + int graph_x_base = lfo_graph_x + 3; + int graph_y_base = lfo_graph_y + 3 + lfo_graph_h / 2; + + const float frames_for_graph = SECS_PER_LFO_OSCILLATION * + mixer::inst()->sampleRate() / 10; + + const float lfo_gray_amount = 1.0f - fabsf( m_lfoAmountKnob->value() ); + p.setPen( QPen( QColor( static_cast( 96 * lfo_gray_amount ), + static_cast( 255 - 159 * lfo_gray_amount ), + static_cast( 128 - 32 * + lfo_gray_amount ) ), + 2 ) ); + + + float osc_frames = m_lfoOscillationFrames; + + if( m_x100Btn->isChecked() ) + { + osc_frames *= 100.0f; + } + +#ifdef QT4 + float old_y = 0; +#else + int old_y = 0; +#endif + for( int x = 0; x <= lfo_graph_w; ++x ) + { + float val = 0.0; + float cur_sample = x*frames_for_graph/lfo_graph_w; + if( static_cast( cur_sample ) > m_lfoPredelayFrames ) + { + float phase = ( cur_sample -= m_lfoPredelayFrames ) / + osc_frames; + switch( m_lfoShape ) + { + case SIN: + val = oscillator::sinSample( phase ); + break; + case TRIANGLE: + val = oscillator::triangleSample( + phase ); + break; + case SAW: + val = oscillator::sawSample( phase ); + break; + case SQUARE: + val = oscillator::squareSample( phase ); + break; + } + if( (Uint32) cur_sample <= m_lfoAttackFrames ) + { + val *= cur_sample / m_lfoAttackFrames; + } + } +#ifdef QT4 + float cur_y = -lfo_graph_h / 2.0f * val; + p.drawLine( QLineF( graph_x_base + x - 1, graph_y_base + old_y, + graph_x_base + x, + graph_y_base + cur_y ) ); +#else + int cur_y = static_cast( -lfo_graph_h / 2.0f * val ); + p.drawLine( graph_x_base + x - 1, graph_y_base + old_y, + graph_x_base + x, graph_y_base + cur_y ); +#endif + old_y = cur_y; + } + + p.setPen( QColor( 255, 192, 0 ) ); + int ms_per_osc = static_cast( SECS_PER_LFO_OSCILLATION * + m_lfoSpeedKnob->value() * + 1000.0f ); + p.drawText( lfo_graph_x + 4, lfo_graph_y + s_lfoGraph->height() - 6, + tr( "ms/LFO:" ) ); + p.drawText( lfo_graph_x + 52, lfo_graph_y + s_lfoGraph->height() - 6, + QString::number( ms_per_osc ) ); + +#ifndef QT4 + // blit drawn pixmap to actual widget + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void envelopeAndLFOWidget::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() != Qt::LeftButton ) + { + return; + } + + if( QRect( env_graph_x, env_graph_y, s_envGraph->width(), + s_envGraph->height() ).contains( _me->pos() ) == TRUE ) + { + if( m_amountKnob->value() < 1.0f ) + { + m_amountKnob->setValue( 1.0f ); + } + else + { + m_amountKnob->setValue( 0.0f ); + } + updateSampleVars(); + } + else if( QRect( lfo_graph_x, lfo_graph_y, s_lfoGraph->width(), + s_lfoGraph->height() ).contains( _me->pos() ) == TRUE ) + { + if( m_lfoAmountKnob->value() < 1.0f ) + { + m_lfoAmountKnob->setValue( 1.0f ); + } + else + { + m_lfoAmountKnob->setValue( 0.0f ); + } + updateSampleVars(); + } +} + + + + +void envelopeAndLFOWidget::updateSampleVars( void ) +{ + if( !m_busy ) + { + m_busy = TRUE; + + const float frames_per_env_seg = SECS_PER_ENV_SEGMENT * + mixer::inst()->sampleRate(); + Uint32 predelay_frames = static_cast( + frames_per_env_seg * + expKnobVal( m_predelayKnob->value() ) ); + + Uint32 attack_frames = static_cast( frames_per_env_seg * + expKnobVal( m_attackKnob->value() ) ); + + Uint32 hold_frames = static_cast( frames_per_env_seg * + expKnobVal( m_holdKnob->value() ) ); + + Uint32 decay_frames = static_cast( frames_per_env_seg * + expKnobVal( m_decayKnob->value() * + m_sustainKnob->value() ) ); + + m_sustainLevel = 1.0f - m_sustainKnob->value(); + m_amount = m_amountKnob->value(); + if( m_amount >= 0 ) + { + m_amountAdd = ( 1.0f - m_amount ) * + m_valueForZeroAmount; + } + else + { + m_amountAdd = m_valueForZeroAmount; + } + + m_pahdFrames = predelay_frames + attack_frames + hold_frames + + decay_frames; + m_rFrames = static_cast( frames_per_env_seg * + expKnobVal( m_releaseKnob->value() ) ); + + if( static_cast( floorf( m_amount * 1000.0f ) ) == 0 ) + { + //m_pahdFrames = 0; + m_rFrames = 0; + } + + float * new_pahd_env = new float[m_pahdFrames]; + float * new_r_env = new float[m_rFrames]; + + for( Uint32 i = 0; i < predelay_frames; ++i ) + { + new_pahd_env[i] = m_amountAdd; + } + + Uint32 add = predelay_frames; + + for( Uint32 i = 0; i < attack_frames; ++i ) + { + new_pahd_env[add+i] = ( (float)i / attack_frames ) * + m_amount + m_amountAdd; + } + + add += attack_frames; + for( Uint32 i = 0; i < hold_frames; ++i ) + { + new_pahd_env[add+i] = m_amount + m_amountAdd; + } + + add += hold_frames; + for( Uint32 i = 0; i < decay_frames; ++i ) + { + new_pahd_env[add+i] = ( m_sustainLevel + ( 1.0f - + (float)i / decay_frames ) * + ( 1.0f - m_sustainLevel ) ) * + m_amount + m_amountAdd; + } + + delete[] m_pahdEnv; + delete[] m_rEnv; + + m_pahdEnv = new_pahd_env; + m_rEnv = new_r_env; + + for( Uint32 i = 0; i < m_rFrames; ++i ) + { + new_r_env[i] = ( (float)( m_rFrames - i ) / m_rFrames * + m_sustainLevel ) * m_amount; + } + + // save this calculation in real-time-part + m_sustainLevel = m_sustainLevel * m_amount + m_amountAdd; + + + const float frames_per_lfo_oscillation = + SECS_PER_LFO_OSCILLATION * + mixer::inst()->sampleRate(); + m_lfoPredelayFrames = static_cast( + frames_per_lfo_oscillation * + expKnobVal( m_lfoPredelayKnob->value() ) ); + m_lfoAttackFrames = static_cast( + frames_per_lfo_oscillation * + expKnobVal( m_lfoAttackKnob->value() ) ); + m_lfoOscillationFrames = static_cast( + frames_per_lfo_oscillation * + m_lfoSpeedKnob->value() ); + if( m_x100Btn->isChecked() ) + { + m_lfoOscillationFrames /= 100; + } + m_lfoAmount = m_lfoAmountKnob->value() * 0.5f; + + m_used = TRUE; + if( static_cast( floorf( m_lfoAmount * 1000.0f ) ) == 0 ) + { + m_lfoAmountIsZero = TRUE; + if( static_cast( floorf( m_amount * 1000.0f ) ) == + 0 ) + { + m_used = FALSE; + } + } + else + { + m_lfoAmountIsZero = FALSE; + } + + if( m_lfoAmountIsZero == FALSE ) + { + delete[] m_lfoShapeData; + m_lfoShapeData = new float[m_lfoOscillationFrames]; + for( Uint32 frame = 0; frame < m_lfoOscillationFrames; + ++frame ) + { + const float phase = frame / static_cast( + m_lfoOscillationFrames ); + // hope, compiler optimizes well and places + // branches out of loop and generates one loop + // for each branch... + switch( m_lfoShape ) + { + case TRIANGLE: + m_lfoShapeData[frame] = + oscillator::triangleSample( phase ); + break; + + case SQUARE: + m_lfoShapeData[frame] = + oscillator::squareSample( phase ); + break; + + case SAW: + m_lfoShapeData[frame] = + oscillator::sawSample( phase ); + break; + + case SIN: + default: + m_lfoShapeData[frame] = + oscillator::sinSample( phase ); + break; + } + m_lfoShapeData[frame] *= m_lfoAmount; + } + } + m_busy = FALSE; + } +} + + + + +void envelopeAndLFOWidget::x100Toggled( bool ) +{ + songEditor::inst()->setModified(); + updateSampleVars(); +} + + + + +void envelopeAndLFOWidget::updateAfterKnobChange( float ) +{ + update(); +} + + + + +void envelopeAndLFOWidget::lfoSinWaveCh( bool _on ) +{ + if( _on ) + { + m_lfoShape = SIN; + } + songEditor::inst()->setModified(); + + update(); +} + + + + +void envelopeAndLFOWidget::lfoTriangleWaveCh( bool _on ) +{ + if( _on ) + { + m_lfoShape = TRIANGLE; + } + songEditor::inst()->setModified(); + + update(); +} + + + + +void envelopeAndLFOWidget::lfoSawWaveCh( bool _on ) +{ + if( _on ) + { + m_lfoShape = SAW; + } + songEditor::inst()->setModified(); + + update(); +} + + + + +void envelopeAndLFOWidget::lfoSquareWaveCh( bool _on ) +{ + if( _on ) + { + m_lfoShape = SQUARE; + } + songEditor::inst()->setModified(); + + update(); +} + + + + +#undef isChecked +#undef setChecked + + +#include "envelope_and_lfo_widget.moc" + diff --git a/src/core/envelope_tab_widget.cpp b/src/core/envelope_tab_widget.cpp new file mode 100644 index 000000000..c3cd6347c --- /dev/null +++ b/src/core/envelope_tab_widget.cpp @@ -0,0 +1,631 @@ +/* + * envelope_tab_widget.cpp - widget for use in envelope/lfo/filter-tab of + * channel-window + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#define isChecked isOn +#define setChecked setOn + +#endif + + +#include "envelope_tab_widget.h" +#include "envelope_and_lfo_widget.h" +#include "note_play_handle.h" +#include "knob.h" +#include "pixmap_button.h" +#include "group_box.h" +#include "tab_widget.h" +#include "embed.h" +#include "templates.h" + + +const int TARGETS_TABWIDGET_X = 4; +const int TARGETS_TABWIDGET_Y = 5; +const int TARGETS_TABWIDGET_WIDTH = 240; +const int TARGETS_TABWIDGET_HEIGTH = 175; + +const int FILTER_GROUPBOX_X = TARGETS_TABWIDGET_X; +const int FILTER_GROUPBOX_Y = TARGETS_TABWIDGET_Y+TARGETS_TABWIDGET_HEIGTH+5; +const int FILTER_GROUPBOX_WIDTH = TARGETS_TABWIDGET_WIDTH; +const int FILTER_GROUPBOX_HEIGHT = 245-FILTER_GROUPBOX_Y; + +const float CUT_FREQ_MULTIPLIER = 6000.0f; +const float RES_MULTIPLIER = 2.0f; +const float RES_PRECISION = 1000.0f; + + +// names for env- and lfo-targets - first is name being displayed to user +// and second one is used internally, e.g. for saving/restoring settings +const QString targetNames[envelopeTabWidget::TARGET_COUNT][2] = +{ + { envelopeTabWidget::tr( "VOLUME" ), "vol" }, +/* envelopeTabWidget::tr( "Pan" ), + envelopeTabWidget::tr( "Pitch" ),*/ + { envelopeTabWidget::tr( "CUTOFF" ), "cut" }, + { envelopeTabWidget::tr( "Q/RESO" ), "res" } +} ; + + + +envelopeTabWidget::envelopeTabWidget( channelTrack * _channel_track, + QWidget * _parent ) : + QWidget( _parent ), + settings(), + m_channelTrack( _channel_track ) +{ + + m_targetsTabWidget = new tabWidget( tr( "TARGET" ), this ); + m_targetsTabWidget->setGeometry( TARGETS_TABWIDGET_X, + TARGETS_TABWIDGET_Y, + TARGETS_TABWIDGET_WIDTH, + TARGETS_TABWIDGET_HEIGTH ); +#ifdef QT4 + m_targetsTabWidget->setWhatsThis( +#else + QWhatsThis::add( m_targetsTabWidget, +#endif + tr( "These tabs contain envelopes. They're very important for " + "modifying a sound, for not saying that they're almost " + "always neccessary for substractive synthesis. For " + "example if you have a volume-envelope, you can set " + "when the sound should have which volume-level. " + "Maybe you want to create some soft strings. Then your " + "sound has to fade in and out very softly. This can be " + "done by setting a large attack- and release-time. " + "It's the same for other envelope-targets like " + "panning, cutoff-frequency of used filter and so on. " + "Just monkey around with it! You can really make cool " + "sounds out of a saw-wave with just some " + "envelopes...!" ) ); + + for( int i = 0; i < TARGET_COUNT; ++i ) + { + float value_for_zero_amount = 0.0; + if( i == VOLUME ) + { + value_for_zero_amount = 1.0; + } + m_envLFOWidgets[i] = new envelopeAndLFOWidget( + value_for_zero_amount, m_targetsTabWidget ); + m_targetsTabWidget->addTab( m_envLFOWidgets[i], + tr( targetNames[i][0] +#ifdef QT4 + .toAscii().constData() +#endif + ) ); +/* +#ifdef QT4 + .toAscii().constData() +#endif + ) );*/ + } + + + m_filterGroupBox = new groupBox( tr( "FILTER" ), this ); + m_filterGroupBox->setGeometry( FILTER_GROUPBOX_X, FILTER_GROUPBOX_Y, + FILTER_GROUPBOX_WIDTH, + FILTER_GROUPBOX_HEIGHT ); + + m_filterComboBox = new QComboBox( m_filterGroupBox ); + m_filterComboBox->setGeometry( 20, 20, 100, 22 ); + m_filterComboBox->setFont( pointSize<8>( m_filterComboBox->font() ) ); + + + m_filterComboBox->addItem( tr( "LowPass" ) ); + m_filterComboBox->addItem( tr( "HiPass" ) ); + m_filterComboBox->addItem( tr( "BandPass csg" ) ); + m_filterComboBox->addItem( tr( "BandPass czpg" ) ); + m_filterComboBox->addItem( tr( "Notch" ) ); + m_filterComboBox->addItem( tr( "Allpass" ) ); + m_filterComboBox->addItem( tr( "Moog" ) ); + m_filterComboBox->addItem( tr( "2x LowPass" ) ); + m_filterComboBox->addItem( tr( "2x Moog" ) ); + +#ifdef QT4 + m_filterComboBox->setWhatsThis( +#else + QWhatsThis::add( m_filterComboBox, +#endif + tr( "Here you can select the built-in filter you want to use " + "in this channel. Filters are very important for " + "changing the characteristics of a sound." ) ); + + + m_filterCutKnob = new knob( knobBright_26, m_filterGroupBox, tr( + "cutoff-frequency" ) ); + m_filterCutKnob->setLabel( tr( "CUTOFF" ) ); + m_filterCutKnob->setRange( 0.0, 16000.0, 1.0 ); + m_filterCutKnob->move( 140, 18 ); + m_filterCutKnob->setValue( 16000.0, TRUE ); + m_filterCutKnob->setHintText( tr( "cutoff-frequency:" ) + " ", " " + + tr( "Hz" ) ); +#ifdef QT4 + m_filterCutKnob->setWhatsThis( +#else + QWhatsThis::add( m_filterCutKnob, +#endif + tr( "Use this knob for setting the cutoff-frequency for the " + "selected filter. The cutoff-frequency specifies the " + "frequency for cutting the signal by a filter. For " + "example a lowpass-filter cuts all frequencies above " + "the cutoff-frequency. A highpass-filter cuts all " + "frequencies below cutoff-frequency and so on..." ) ); + + m_filterResKnob = new knob( knobBright_26, m_filterGroupBox, tr( + "Q/Resonance" ) ); + m_filterResKnob->setLabel( tr( "Q/RESO" ) ); + m_filterResKnob->setRange( 0.01, 10.0, 0.01 ); + m_filterResKnob->move( 190, 18 ); + m_filterResKnob->setValue( 0.5, TRUE ); + m_filterResKnob->setHintText( tr( "Q/Resonance:" ) + " ", "" ); +#ifdef QT4 + m_filterResKnob->setWhatsThis( +#else + QWhatsThis::add( m_filterResKnob, +#endif + tr( "Use this knob for setting Q/Resonance for the selected " + "filter. Q/Resonance tells the filter, how much it " + "should amplify frequencies near Cutoff-frequency." ) ); + +} + + + + +envelopeTabWidget::~envelopeTabWidget() +{ + delete m_targetsTabWidget; +} + + + + +float FASTCALL envelopeTabWidget::volumeLevel( notePlayHandle * _n, + Uint32 _frame ) +{ + Uint32 release_begin = _frame - _n->releaseFramesDone() + + _n->framesBeforeRelease(); + + if( _n->released() == FALSE ) + { + release_begin += mixer::inst()->framesPerAudioBuffer(); + } + + return( m_envLFOWidgets[VOLUME]->level( _frame, release_begin, 0 ) ); +} + + + + +void envelopeTabWidget::processAudioBuffer( sampleFrame * _ab, Uint32 _frames, + notePlayHandle * _n ) +{ + Uint32 total_frames = _n->totalFramesPlayed(); + Uint32 release_begin = total_frames - _n->releaseFramesDone() + + _n->framesBeforeRelease(); + + if( _n->released() == FALSE ) + { + release_begin += mixer::inst()->framesPerAudioBuffer(); + } + + // because of optimizations, there's special code for several cases: + // - volume-, cut- and res-lfo/envelope active + // - volume- and cut-lfo/envelope active + // - volume- and res-lfo/envelope active + // - cut-lfo/envelope active + // - res-lfo/envelope active + // - volume-lfo/envelope active + // - no lfo/envelope active but filter is used + // now there's a lot of similar code but I didn't found a way to + // generalize it yet... may be later we could do that + // by using preprocessor and macro-expansion... (like in oscillator.cpp) + + // only use filter, if it is really needed + + if( _n->m_filter == NULL ) + { + _n->m_filter = new basicFilters<>( + mixer::inst()->sampleRate() ); + } + + if( /*m_filterState->isChecked()*/ m_filterGroupBox->isActive() ) + { + int old_filter_cut = 0; + int old_filter_res = 0; + + basicFilters<>::filterTypes filter = static_cast::filterTypes>( m_filterComboBox-> +#ifdef QT4 + currentIndex() +#else + currentItem() +#endif + ); + + if( m_envLFOWidgets[VOLUME]->used() && + m_envLFOWidgets[CUT]->used() && + m_envLFOWidgets[RES]->used() ) + { + for( Uint32 frame = 0; frame < _frames; + ++frame, ++total_frames ) + { + float new_cut_val = m_envLFOWidgets[CUT]->level( total_frames, release_begin, frame ); + new_cut_val = envelopeAndLFOWidget::expKnobVal( new_cut_val ) * CUT_FREQ_MULTIPLIER + + m_filterCutKnob->value(); + + float new_res_val = m_filterResKnob->value() + RES_MULTIPLIER * + m_envLFOWidgets[RES]->level( total_frames, release_begin, frame ); + + if( static_cast( new_cut_val ) != old_filter_cut || + static_cast( new_res_val*RES_PRECISION ) != old_filter_res ) + { + _n->m_filter->calcFilterCoeffs( filter, new_cut_val, new_res_val ); + old_filter_cut = static_cast( new_cut_val ); + old_filter_res = static_cast( new_res_val*RES_PRECISION ); + } + + float vol_level = m_envLFOWidgets[VOLUME]->level( total_frames, release_begin, frame ); + vol_level = vol_level*vol_level; + + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = vol_level * _n->m_filter->update( _ab[frame][chnl], chnl ); + } + } + } + else if( m_envLFOWidgets[VOLUME]->used() && m_envLFOWidgets[CUT]->used() ) + { + for( Uint32 frame = 0; frame < _frames; ++frame, ++total_frames ) + { + float new_cut_val = m_envLFOWidgets[CUT]->level( total_frames, release_begin, frame ); + new_cut_val = envelopeAndLFOWidget::expKnobVal( new_cut_val ) * CUT_FREQ_MULTIPLIER + + m_filterCutKnob->value(); + + if( static_cast( new_cut_val ) != old_filter_cut ) + { + _n->m_filter->calcFilterCoeffs( filter, new_cut_val, m_filterResKnob->value() ); + old_filter_cut = static_cast( new_cut_val ); + } + + float vol_level = m_envLFOWidgets[VOLUME]->level( total_frames, release_begin, frame ); + vol_level = vol_level*vol_level; + + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = vol_level * _n->m_filter->update( _ab[frame][chnl], chnl ); + } + } + } + else if( m_envLFOWidgets[VOLUME]->used() && m_envLFOWidgets[RES]->used() ) + { + + for( Uint32 frame = 0; frame < _frames; ++frame, ++total_frames ) + { + float new_res_val = m_filterResKnob->value() + RES_MULTIPLIER * + m_envLFOWidgets[RES]->level( total_frames, release_begin, frame ); + + if( static_cast( new_res_val*RES_PRECISION ) != old_filter_res ) + { + _n->m_filter->calcFilterCoeffs( filter, m_filterCutKnob->value(), new_res_val ); + old_filter_res = static_cast( new_res_val*RES_PRECISION ); + } + + float vol_level = m_envLFOWidgets[VOLUME]->level( total_frames, release_begin, frame ); + vol_level = vol_level*vol_level; + + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = vol_level * _n->m_filter->update( _ab[frame][chnl], chnl ); + } + } + + } + else if( m_envLFOWidgets[CUT]->used() ) + { + for( Uint32 frame = 0; frame < _frames; ++frame, ++total_frames ) + { + float new_cut_val = m_envLFOWidgets[CUT]->level( total_frames, release_begin, frame ); + new_cut_val = envelopeAndLFOWidget::expKnobVal( new_cut_val ) * CUT_FREQ_MULTIPLIER + + m_filterCutKnob->value(); + + if( static_cast( new_cut_val ) != old_filter_cut ) + { + _n->m_filter->calcFilterCoeffs( filter, new_cut_val, m_filterResKnob->value() ); + old_filter_cut = static_cast( new_cut_val ); + } + + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = _n->m_filter->update( _ab[frame][chnl], chnl ); + } + } + } + else if( m_envLFOWidgets[RES]->used() ) + { + for( Uint32 frame = 0; frame < _frames; ++frame, ++total_frames ) + { + float new_res_val = m_filterResKnob->value() + RES_MULTIPLIER * + m_envLFOWidgets[RES]->level( total_frames, release_begin, frame ); + + if( static_cast( new_res_val*RES_PRECISION ) != old_filter_res ) + { + _n->m_filter->calcFilterCoeffs( filter, m_filterCutKnob->value(), new_res_val ); + old_filter_res = static_cast( new_res_val*RES_PRECISION ); + } + + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = _n->m_filter->update( _ab[frame][chnl], chnl ); + } + } + } + else if( m_envLFOWidgets[VOLUME]->used() ) + { + _n->m_filter->calcFilterCoeffs( filter, m_filterCutKnob->value(), m_filterResKnob->value() ); + + for( Uint32 frame = 0; frame < _frames; ++frame, ++total_frames ) + { + float vol_level = m_envLFOWidgets[VOLUME]->level( total_frames, release_begin, frame ); + vol_level = vol_level*vol_level; + + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = vol_level * _n->m_filter->update( _ab[frame][chnl], chnl ); + } + } + } + else + { + _n->m_filter->calcFilterCoeffs( filter, m_filterCutKnob->value(), m_filterResKnob->value() ); + + for( Uint32 frame = 0; frame < _frames; ++frame, ++total_frames ) + { + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = _n->m_filter->update( _ab[frame][chnl], chnl ); + } + } + } + } + else if( m_envLFOWidgets[VOLUME]->used() /*&& m_envLFOWidgets[PANNING]->used() == FALSE*/ ) + { + // only use volume-envelope... + for( Uint32 frame = 0; frame < _frames; ++frame, ++total_frames ) + { + float vol_level = m_envLFOWidgets[VOLUME]->level( total_frames, release_begin, frame ); + vol_level = vol_level*vol_level; + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = vol_level * _ab[frame][chnl]; + } + } + } +/* else if( m_envLFOWidgets[VOLUME]->used() == FALSE && m_envLFOWidgets[PANNING]->used() ) + { + // only use panning-envelope... + for( Uint32 frame = 0; frame < _frames; ++frame, ++total_frames ) + { + float vol_level = m_envLFOWidgets[PANNING]->level( total_frames, release_begin, frame ); + vol_level = vol_level*vol_level; + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _ab[frame][chnl] = vol_level * _ab[frame][chnl]; + } + } + }*/ +} + + + + +Uint32 envelopeTabWidget::envFrames( void ) +{ + Uint32 ret_val = m_envLFOWidgets[VOLUME]->m_pahdFrames; + + for( int i = VOLUME+1; i < TARGET_COUNT; ++i ) + { + if( m_envLFOWidgets[i]->used() && + m_envLFOWidgets[i]->m_pahdFrames > ret_val ) + { + ret_val = m_envLFOWidgets[i]->m_pahdFrames; + } + } + return( ret_val ); +} + + + + +Uint32 envelopeTabWidget::releaseFrames( void ) +{ + Uint32 ret_val = m_envLFOWidgets[VOLUME]->m_rFrames; + for( int i = VOLUME+1; i < TARGET_COUNT; ++i ) + { + if( m_envLFOWidgets[i]->used() && + m_envLFOWidgets[i]->m_rFrames > ret_val ) + { + ret_val = m_envLFOWidgets[i]->m_rFrames; + } + } + return( ret_val ); +} + + + + +void envelopeTabWidget::saveSettings( QDomDocument & _doc, + QDomElement & _parent ) +{ + QDomElement etw_de = _doc.createElement( nodeName() ); +#ifdef QT4 + etw_de.setAttribute( "ftype", QString::number( + m_filterComboBox->currentIndex() ) ); +#else + etw_de.setAttribute( "ftype", QString::number( + m_filterComboBox->currentItem() ) ); +#endif + etw_de.setAttribute( "fcut", QString::number( + m_filterCutKnob->value() ) ); + etw_de.setAttribute( "fres", QString::number( + m_filterResKnob->value() ) ); + etw_de.setAttribute( "fwet", QString::number( + /*m_filterState->isChecked()*/ + m_filterGroupBox->isActive() ) ); + _parent.appendChild( etw_de ); + + for( int i = 0; i < TARGET_COUNT; ++i ) + { + QDomElement target_de = _doc.createElement( + m_envLFOWidgets[i]->nodeName() + + targetNames[i][1].toLower() ); + m_envLFOWidgets[i]->saveSettings( _doc, target_de ); + etw_de.appendChild( target_de ); + } +} + + + + +void envelopeTabWidget::loadSettings( const QDomElement & _this ) +{ +#ifdef QT4 + m_filterComboBox->setCurrentIndex( _this.attribute( "ftype" ).toInt() ); +#else + m_filterComboBox->setCurrentItem( _this.attribute( "ftype" ).toInt() ); +#endif + m_filterCutKnob->setValue( _this.attribute( "fcut" ).toFloat() ); + m_filterResKnob->setValue( _this.attribute( "fres" ).toFloat() ); +/* m_filterState->setChecked( _this.attribute( "fwet" ).toInt() );*/ + m_filterGroupBox->setState( _this.attribute( "fwet" ).toInt() ); + + QDomNode node = _this.firstChild(); + while( !node.isNull() ) + { + if( node.isElement() ) + { + for( int i = 0; i < TARGET_COUNT; ++i ) + { + if( node.nodeName() == + m_envLFOWidgets[i]->nodeName() + + targetNames[i][1].toLower() ) + { + m_envLFOWidgets[i]->loadSettings( + node.toElement() ); + } + } + } + node = node.nextSibling(); + } +} + + + +#undef isChecked +#undef setChecked + + +#include "envelope_tab_widget.moc" + + + +/* + + +const long double coeff[5][11]= { +{ + ///A +8.11044e-06, +8.943665402, -36.83889529, 92.01697887, -154.337906, 181.6233289, +-151.8651235, 89.09614114, -35.10298511, 8.388101016, -0.923313471 +}, +{ +///E +4.36215e-06, +8.90438318, -36.55179099, 91.05750846, -152.422234, 179.1170248, +-149.6496211,87.78352223, -34.60687431, 8.282228154, -0.914150747 +}, +{ +///I +3.33819e-06, +8.893102966, -36.49532826, 90.96543286, -152.4545478, 179.4835618, +-150.315433, 88.43409371, -34.98612086, 8.407803364, -0.932568035 +}, +{ + ///O +1.13572e-06, +8.994734087, -37.2084849, 93.22900521, -156.6929844, 184.596544, +-154.3755513, 90.49663749, -35.58964535, 8.478996281, -0.929252233 +}, +{ +///U +4.09431e-07, +8.997322763, -37.20218544, 93.11385476, -156.2530937, 183.7080141, +-153.2631681, 89.59539726, -35.12454591, 8.338655623, -0.910251753 +} +}; +//----------------------------------------------------------------------------- +static long double memory[10]={0,0,0,0,0,0,0,0,0,0}; +//------------------------------------------------------------------------------ +inline float formant_filter(float in, int vowelnum) +{ + float res= (float) ( coeff[vowelnum][0] *in + + coeff[vowelnum][1] *memory[0] + + coeff[vowelnum][2] *memory[1] + + coeff[vowelnum][3] *memory[2] + + coeff[vowelnum][4] *memory[3] + + coeff[vowelnum][5] *memory[4] + + coeff[vowelnum][6] *memory[5] + + coeff[vowelnum][7] *memory[6] + + coeff[vowelnum][8] *memory[7] + + coeff[vowelnum][9] *memory[8] + + coeff[vowelnum][10] *memory[9] ); + +memory[9]= memory[8]; +memory[8]= memory[7]; +memory[7]= memory[6]; +memory[6]= memory[5]; +memory[5]= memory[4]; +memory[4]= memory[3]; +memory[3]= memory[2]; +memory[2]= memory[1]; +memory[1]= memory[0]; +memory[0]=(long double) res; +return res; +} + +*/ diff --git a/src/core/export_project_dialog.cpp b/src/core/export_project_dialog.cpp new file mode 100644 index 000000000..6d4dc90cd --- /dev/null +++ b/src/core/export_project_dialog.cpp @@ -0,0 +1,481 @@ +/* + * export_project_dialog.cpp - implementation of dialog for exporting project + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + + +#include "export_project_dialog.h" +#include "song_editor.h" +#include "embed.h" + +#include "audio_file_wave.h" +#include "audio_file_ogg.h" + + +fileEncodeDevice fileEncodeDevices[] = +{ + + { WAVE_FILE, QT_TRANSLATE_NOOP( "exportProjectDialog", + "Uncompressed Wave-File (*.wav)" ), + ".wav", &audioFileWave::getInst }, +#ifdef HAVE_VORBIS_CODEC_H + { OGG_FILE, QT_TRANSLATE_NOOP( "exportProjectDialog", + "Compressed OGG-File (*.ogg)" ), + ".ogg", &audioFileOgg::getInst }, +#endif + // ... insert your own file-encoder-infos here... may be one day the + // user can add own encoders inside the program... + + { NULL_FILE, NULL, NULL, NULL } + +} ; + + + +const int LABEL_MARGIN = 6; +const int LABEL_X = 10; +const int LABEL_WIDTH = 48; +const int TYPE_STUFF_Y = 10; +const int TYPE_HEIGHT = 26; +const int TYPE_COMBO_WIDTH = 256; +const int KBPS_STUFF_Y = TYPE_STUFF_Y + TYPE_HEIGHT + LABEL_MARGIN + 6; +const int KBPS_HEIGHT = TYPE_HEIGHT; +const int KBPS_COMBO_WIDTH = 64; +const int HQ_MODE_CB_Y = KBPS_STUFF_Y + KBPS_HEIGHT + LABEL_MARGIN; +const int HQ_MODE_CB_HEIGHT = TYPE_HEIGHT; +const int HQ_MODE_CB_WIDTH = 300; +const int CTL_BUTTONS_Y = HQ_MODE_CB_Y + HQ_MODE_CB_HEIGHT + LABEL_MARGIN + 6; +const int CTL_BUTTONS_HEIGHT = 30; +const int CTL_BUTTONS_WIDTH = 120; +const int HOURGLASS_X = LABEL_X; +const int HOURGLASS_Y = 24; +const int HOURGLASS_WIDTH = 44; +const int HOURGLASS_HEIGHT = 56; + +const int EPB_X = LABEL_X + 60; +const int EPB_Y = HOURGLASS_Y; +const int EPB_WIDTH = 260; +const int EPB_HEIGHT = 24; +const int CANCEL_X_WHILE_EXPORT = 136; +const int CANCEL_Y_WHILE_EXPORT = EPB_Y + EPB_HEIGHT + 32; + + +Sint16 exportProjectDialog::s_availableBitrates[] = +{ + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 256, + 320, + -1 +}; + + +// TODO: rewrite that crap using layouts!! + +exportProjectDialog::exportProjectDialog( const QString & _file_name, + QWidget * _parent ) : + QDialog( _parent ), + m_fileName( _file_name ), + m_hourglassLbl( NULL ), + m_exportProgressBar( NULL ), + m_deleteFile( FALSE ), + m_oldProgressVal( -1 ) +{ +#ifdef QT4 + m_fileType = getFileTypeFromExtension( "." + + QFileInfo( _file_name + ).completeSuffix() ); +#else + m_fileType = getFileTypeFromExtension( "." + QFileInfo( _file_name + ).extension() ); +#endif + setWindowTitle( tr( "Export project to %1" ).arg( + QFileInfo( _file_name ).fileName() ) ); + + // type-ui-stuff + m_typeLbl = new QLabel( tr( "Type:" ), this ); + m_typeLbl->setGeometry( LABEL_X, TYPE_STUFF_Y, LABEL_WIDTH, + TYPE_HEIGHT ); + + m_typeCombo = new QComboBox( this ); + m_typeCombo->setGeometry( LABEL_X + LABEL_WIDTH+LABEL_MARGIN, + TYPE_STUFF_Y, TYPE_COMBO_WIDTH, + TYPE_HEIGHT ); + connect( m_typeCombo, SIGNAL( activated( const QString & ) ), this, + SLOT( changedType( const QString & ) ) ); + + int idx = 0; + while( fileEncodeDevices[idx].m_fileType != NULL_FILE ) + { + m_typeCombo->addItem( + tr( fileEncodeDevices[idx].m_description ) ); + ++idx; + } +#ifdef QT4 + m_typeCombo->setCurrentIndex( m_typeCombo->findText( tr( + fileEncodeDevices[m_fileType].m_description ) ) ); +#else + m_typeCombo->setCurrentText( tr( + fileEncodeDevices[m_fileType].m_description ) ); +#endif + + + // kbps-ui-stuff + m_kbpsLbl = new QLabel( tr( "kbps:" ), this ); + m_kbpsLbl->setGeometry( LABEL_X, KBPS_STUFF_Y, LABEL_WIDTH, + KBPS_HEIGHT ); + + m_kbpsCombo = new QComboBox( this ); + m_kbpsCombo->setGeometry( LABEL_X + LABEL_WIDTH + LABEL_MARGIN, + KBPS_STUFF_Y, KBPS_COMBO_WIDTH, + KBPS_HEIGHT ); + + idx = 0; + while( s_availableBitrates[idx] != -1 ) + { + m_kbpsCombo->addItem( QString::number( + s_availableBitrates[idx] ) ); + ++idx; + } +#ifdef QT4 + m_typeCombo->setCurrentIndex( m_typeCombo->findText( + QString::number( 128 ) ) ); +#else + m_kbpsCombo->setCurrentText( QString::number( 128 ) ); +#endif + + + m_vbrCb = new QCheckBox( tr( "variable bitrate" ), this ); + m_vbrCb->setGeometry( LABEL_X + LABEL_WIDTH + 3 * LABEL_MARGIN + + KBPS_COMBO_WIDTH, KBPS_STUFF_Y + 3, 190, 20 ); + m_vbrCb->setChecked( TRUE ); + + + m_hqmCb = new QCheckBox( tr( "use high-quality-mode (recommened)" ), + this ); + m_hqmCb->setGeometry( LABEL_X, HQ_MODE_CB_Y + 3, HQ_MODE_CB_WIDTH, + HQ_MODE_CB_HEIGHT ); + m_hqmCb->setChecked( TRUE ); + + + m_exportBtn = new QPushButton( embed::getIconPixmap( "apply" ), + tr( "Export" ), this ); + m_exportBtn->setGeometry( LABEL_X + LABEL_WIDTH + LABEL_MARGIN, + CTL_BUTTONS_Y, CTL_BUTTONS_WIDTH, + CTL_BUTTONS_HEIGHT ); + connect( m_exportBtn, SIGNAL( clicked() ), this, + SLOT( exportBtnClicked() ) ); + + m_cancelBtn = new QPushButton( embed::getIconPixmap( "cancel" ), + tr( "Cancel" ), this ); + m_cancelBtn->setGeometry( LABEL_X + LABEL_WIDTH + 2 * LABEL_MARGIN + + CTL_BUTTONS_WIDTH, CTL_BUTTONS_Y, + CTL_BUTTONS_WIDTH, CTL_BUTTONS_HEIGHT ); + connect( m_cancelBtn, SIGNAL( clicked() ), this, + SLOT( cancelBtnClicked() ) ); + +} + + + + +exportProjectDialog::~exportProjectDialog() +{ +} + + + + +// little help-function for getting file-type from a file-extension (only for +// registered file-encoders) +fileTypes exportProjectDialog::getFileTypeFromExtension( const QString & _ext ) +{ + int idx = 0; + while( fileEncodeDevices[idx].m_fileType != NULL_FILE ) + { + if( QString( fileEncodeDevices[idx].m_extension ) == _ext ) + { + return( fileEncodeDevices[idx].m_fileType ); + } + ++idx; + } + + return( WAVE_FILE ); // default +} + + + + +void exportProjectDialog::keyPressEvent( QKeyEvent * _ke ) +{ + if( _ke->key() == Qt::Key_Escape ) + { + if( songEditor::inst()->exporting() == FALSE ) + { + accept(); + } + else + { + abortProjectExport(); + } + } +} + + + + +void exportProjectDialog::closeEvent (QCloseEvent * _ce) +{ + if( songEditor::inst()->exporting() == TRUE ) + { + abortProjectExport(); + _ce->ignore(); + } +} + + + + +void exportProjectDialog::changedType( const QString & _new_type ) +{ + int idx = 0; + while( fileEncodeDevices[idx].m_fileType != NULL_FILE ) + { + if( tr( fileEncodeDevices[idx].m_description ) == _new_type ) + { + m_fileType = fileEncodeDevices[idx].m_fileType; + break; + } + ++idx; + } +} + + + + +void exportProjectDialog::exportBtnClicked( void ) +{ + int idx = 0; + while( fileEncodeDevices[idx].m_fileType != NULL_FILE ) + { + if( fileEncodeDevices[idx].m_fileType == m_fileType ) + { + bool success_ful = FALSE; + audioDevice * dev = fileEncodeDevices[idx].m_getDevInst( + DEFAULT_SAMPLE_RATE, + DEFAULT_CHANNELS, + success_ful, + m_fileName, + m_vbrCb->isChecked(), + m_kbpsCombo->currentText().toInt(), + m_kbpsCombo->currentText().toInt() - 64, + m_kbpsCombo->currentText().toInt() + 64 + ); + if( success_ful == FALSE ) + { + QMessageBox::information( this, + tr( "Export failed" ), + tr( "The project-export failed, " + "because the output-file/-" + "device could not be opened.\n" + "Make sure, you have write " + "access to the selected " + "file/device!" ), + QMessageBox::Ok ); + return; + } + mixer::inst()->pause(); + mixer::inst()->setAudioDevice( dev, + m_hqmCb->isChecked() ); + songEditor::inst()->startExport(); + mixer::inst()->play(); + break; + } + ++idx; + } + + if( fileEncodeDevices[idx].m_fileType == NULL_FILE ) + { + return; + } + + setWindowTitle( tr( "Exporting project to %1" ).arg( + QFileInfo( m_fileName ).fileName() ) ); + + delete m_typeLbl; + delete m_typeCombo; + delete m_kbpsLbl; + delete m_kbpsCombo; + delete m_vbrCb; + delete m_hqmCb; + delete m_exportBtn; + + m_exportProgressBar = new QProgressBar( this ); + m_exportProgressBar->setGeometry( EPB_X, EPB_Y, EPB_WIDTH, EPB_HEIGHT ); +#ifdef QT4 + m_exportProgressBar->setMaximum( 100 ); +#else + m_exportProgressBar->setTotalSteps( 100 ); +#endif + m_exportProgressBar->show(); + + m_hourglassLbl = new QLabel( this ); + m_hourglassLbl->setPixmap( embed::getIconPixmap( "hourglass" ) ); + m_hourglassLbl->setGeometry( HOURGLASS_X, HOURGLASS_Y, + HOURGLASS_WIDTH, HOURGLASS_HEIGHT ); + m_hourglassLbl->show(); + + m_cancelBtn->move( CANCEL_X_WHILE_EXPORT, CANCEL_Y_WHILE_EXPORT ); + + m_progressBarUpdateTimer = new QTimer( this ); + connect( m_progressBarUpdateTimer, SIGNAL( timeout() ), this, + SLOT( redrawProgressBar() ) ); + m_progressBarUpdateTimer->start( 100 ); + +} + + + + +void exportProjectDialog::cancelBtnClicked( void ) +{ + // is song-export-thread active? + if( songEditor::inst()->exporting() == TRUE ) + { + // then dispose abort of export + abortProjectExport(); + return; + } + + // if the user aborted export-process, the file has to be deleted + if( m_deleteFile ) + { + QFile( m_fileName ).remove(); + } + + // restore window-title + lmmsMainWin::inst()->resetWindowTitle(); + + // let's close us... + accept(); + +} + + + + +// called whenever there's a reason for aborting song-export (like user-input) +void exportProjectDialog::abortProjectExport( void ) +{ + mixer::inst()->pause(); + m_deleteFile = TRUE; + + finishProjectExport(); +} + + + + +void exportProjectDialog::finishProjectExport( void ) +{ + m_progressBarUpdateTimer->stop(); + delete m_progressBarUpdateTimer; + + mixer::inst()->restoreAudioDevice(); + songEditor::inst()->stopExport(); + + mixer::inst()->play(); + + // this method does the final cleanup... + cancelBtnClicked(); +} + + + + +void exportProjectDialog::redrawProgressBar( void ) +{ + if( m_progressVal != m_oldProgressVal ) + { +#ifdef QT4 + m_exportProgressBar->setValue( m_progressVal ); +#else + m_exportProgressBar->setProgress( m_progressVal ); +#endif + // update lmms-main-win-caption + lmmsMainWin::inst()->setWindowTitle( tr( "Rendering:" ) + " " + + QString::number( m_progressVal ) + "%" ); + m_oldProgressVal = m_progressVal; + } + + if( songEditor::inst()->exportDone() == TRUE || + songEditor::inst()->exporting() == FALSE ) + { + finishProjectExport(); + } + +} + + + + +void exportProjectDialog::updateProgressBar( int _new_val ) +{ + m_progressVal = _new_val; +} + + + +#include "export_project_dialog.moc" + diff --git a/src/core/main.cpp b/src/core/main.cpp new file mode 100644 index 000000000..fc9392c56 --- /dev/null +++ b/src/core/main.cpp @@ -0,0 +1,168 @@ +/* + * main.cpp - just main.cpp which is starting up app... + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#if QT_VERSION >= 0x030200 +#include +#endif + +#endif + +#include "lmms_main_win.h" +#include "embed.h" +#include "config_mgr.h" +#include "export_project_dialog.h" +#include "song_editor.h" +#include "templates.h" + + +#if QT_VERSION >= 0x030100 +int splash_alignment_flags = Qt::AlignTop | Qt::AlignLeft; +#endif + +QString file_to_load; +QString file_to_render; + + +int main( int argc, char * * argv ) +{ + QApplication app( argc, argv ); + + for( int i = 1; i < app.argc(); ++i ) + { + if( QString( app.argv()[i] ) == "--version" || + QString( app.argv()[i] ) == "-v" ) + { + printf( "\n%s\n\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public\n" + "License as published by the Free Software Foundation; either\n" + "version 2 of the License, or (at your option) any later version.\n\n", + PACKAGE_STRING ); + return( 0 ); + } + else if( app.argc() > i && + ( QString( app.argv()[i] ) == "--render" || + QString( app.argv()[i] ) == "-r" ) ) + { + file_to_load = QString( app.argv()[i+1] ); + file_to_render = QString( app.argv()[i+1] ) + ".wav"; + } + else + { + file_to_load = app.argv()[i]; + } + } + + QString pos = +#ifdef QT4 + QLocale::system().name().left( 2 ); +#else + QString( QTextCodec::locale() ).section( '_', 0, 0 ); +#endif + // load translation for Qt-widgets/-dialogs + embed::loadTranslation( QString( "qt_" ) + pos ); + // load actual translation for LMMS + embed::loadTranslation( pos ); + +#ifdef QT4 + app.setFont( pointSize<10>( app.font() ) ); +#endif + + + if( !configManager::inst()->loadConfigFile() ) + { + return( -1 ); + } + + QPalette pal = app.palette(); +#ifdef QT4 + pal.setColor( QPalette::Background, QColor( 128, 128, 128 ) ); + pal.setColor( QPalette::Foreground, QColor( 240, 240, 240 ) ); + pal.setColor( QPalette::Base, QColor( 128, 128, 128 ) ); + pal.setColor( QPalette::Text, QColor( 224, 224, 224 ) ); + pal.setColor( QPalette::Button, QColor( 160, 160, 160 ) ); + pal.setColor( QPalette::ButtonText, QColor( 255, 255, 255 ) ); + pal.setColor( QPalette::Highlight, QColor( 255, 224, 224 ) ); + pal.setColor( QPalette::HighlightedText, QColor( 0, 0, 0 ) ); +#else + pal.setColor( QColorGroup::Background, QColor( 128, 128, 128 ) ); + pal.setColor( QColorGroup::Foreground, QColor( 240, 240, 240 ) ); + pal.setColor( QColorGroup::Base, QColor( 128, 128, 128 ) ); + pal.setColor( QColorGroup::Text, QColor( 224, 224, 224 ) ); + pal.setColor( QColorGroup::Button, QColor( 160, 160, 160 ) ); + pal.setColor( QColorGroup::ButtonText, QColor( 255, 255, 255 ) ); + pal.setColor( QColorGroup::Highlight, QColor( 224, 224, 224 ) ); + pal.setColor( QColorGroup::HighlightedText, QColor( 0, 0, 0 ) ); +#endif + app.setPalette( pal ); + + +#if QT_VERSION >= 0x030200 + // init splash screen + QPixmap splash = embed::getIconPixmap( "splash" ); + lmmsMainWin::s_splashScreen = new QSplashScreen( splash ); + lmmsMainWin::s_splashScreen->show(); + + lmmsMainWin::s_splashScreen->showMessage( lmmsMainWin::tr( + "Setting up main-" + "window and " + "workspace..." ), + splash_alignment_flags, + Qt::white ); +#endif + +#ifndef QT4 + app.setMainWidget( lmmsMainWin::inst() ); +#endif + lmmsMainWin::inst()->showMaximized(); + + +#if QT_VERSION >= 0x030200 + delete lmmsMainWin::s_splashScreen; + lmmsMainWin::s_splashScreen = NULL; +#endif + if( file_to_render != "" ) + { + exportProjectDialog * e = new exportProjectDialog( + file_to_render, + lmmsMainWin::inst() ); + songEditor::inst()->setExportProjectDialog( e ); + e->show(); + e->exportBtnClicked(); + } + return( app.exec() ); +} + diff --git a/src/core/mixer.cpp b/src/core/mixer.cpp new file mode 100644 index 000000000..c8c05398b --- /dev/null +++ b/src/core/mixer.cpp @@ -0,0 +1,698 @@ +/* + * mixer.cpp - audio-device-independent mixer for LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "mixer.h" +#include "play_handle.h" +#include "song_editor.h" +#include "templates.h" +#include "envelope_and_lfo_widget.h" +#include "buffer_allocator.h" +#include "debug.h" +#include "config_mgr.h" + +#include "audio_device.h" +#include "midi_device.h" + +// platform-specific audio-interface-classes +#include "audio_alsa.h" +#include "audio_jack.h" +#include "audio_oss.h" +#include "audio_sdl.h" +#include "audio_dummy.h" + +// platform-specific midi-interface-classes +#include "midi_alsa_raw.h" +#include "midi_oss.h" +#include "midi_dummy.h" + + + +Uint32 SAMPLE_RATES[QUALITY_LEVELS] = { 44100, 88200 } ; + +mixer * mixer::s_instanceOfMe = NULL; + + +mixer::mixer() : +#ifndef QT4 + QObject(), +#endif + QThread(), + m_silence(), +#ifndef DISABLE_SURROUND + m_surroundSilence(), +#endif + m_framesPerAudioBuffer( DEFAULT_BUFFER_SIZE ), + m_buffer1( NULL ), + m_buffer2( NULL ), + m_curBuf( NULL ), + m_nextBuf( NULL ), + m_discardCurBuf( FALSE ), + m_qualityLevel( DEFAULT_QUALITY_LEVEL ), + m_masterOutput( 1.0f ), + m_quit( FALSE ), + m_audioDev( NULL ), + m_oldAudioDev( NULL ) +{ + // small hack because code calling mixer::inst() is called out of ctor + s_instanceOfMe = this; + + if( configManager::inst()->value( "mixer", "framesperaudiobuffer" + ).toInt() >= 32 ) + { + m_framesPerAudioBuffer = configManager::inst()->value( "mixer", + "framesperaudiobuffer" ).toInt(); + } + else + { + configManager::inst()->setValue( "mixer", + "framesperaudiobuffer", + QString::number( m_framesPerAudioBuffer ) ); + } + + m_buffer1 = bufferAllocator::alloc( + m_framesPerAudioBuffer ); + m_buffer2 = bufferAllocator::alloc( + m_framesPerAudioBuffer ); + + m_curBuf = m_buffer1; + m_nextBuf = m_buffer2; + + + m_audioDev = tryAudioDevices(); + m_midiDev = tryMIDIDevices(); + + + for( int i = 0; i < MAX_SAMPLE_PACKETS; ++i ) + { + m_samplePackets[i].m_buffer = NULL; + m_samplePackets[i].m_state = samplePacket::UNUSED; + } + + m_silence = bufferAllocator::alloc( + m_framesPerAudioBuffer ); + m_surroundSilence = bufferAllocator::alloc( + m_framesPerAudioBuffer ); + + for( Uint32 frame = 0; frame < m_framesPerAudioBuffer; ++frame ) + { + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + m_silence[frame][chnl] = 0.0f; + } +#ifndef DISABLE_SURROUND + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; ++chnl ) + { + m_surroundSilence[frame][chnl] = 0.0f; + } +#endif + } + + // now clear our two output-buffers before using them... + clearAudioBuffer( m_buffer1, m_framesPerAudioBuffer ); + clearAudioBuffer( m_buffer2, m_framesPerAudioBuffer ); + +} + + + + +mixer::~mixer() +{ + delete m_audioDev; + + bufferAllocator::free( m_buffer1 ); + bufferAllocator::free( m_buffer2 ); + + for( int i = 0; i < MAX_SAMPLE_PACKETS; ++i ) + { + if( m_samplePackets[i].m_state != samplePacket::UNUSED ) + { + bufferAllocator::free( m_samplePackets[i].m_buffer ); + } + } +} + + + + +void mixer::quitThread( void ) +{ + // make sure there're no mutexes locked anymore... + m_safetySyncMutex.unlock(); + m_devMutex.unlock(); + + // now tell mixer-thread to quit + m_quit = TRUE; + + wait( 1000 ); + terminate(); +} + + + + +void mixer::run( void ) +{ + + while( m_quit == FALSE ) + { + + // remove all play-handles that have to be deleted and delete + // them if they still exist... + // maybe this algorithm could be optimized... + while( !m_playHandlesToRemove.isEmpty() ) + { + playHandleVector::iterator it = m_playHandles.begin(); + + while( it != m_playHandles.end() ) + { + if( *it == m_playHandlesToRemove.first() ) + { + m_playHandles.erase( it ); + delete m_playHandlesToRemove.first(); + break; + } + ++it; + } + + m_playHandlesToRemove.erase( + m_playHandlesToRemove.begin() ); + + } + + // now we have to make sure no other thread does anything bad + // while we're acting... + m_safetySyncMutex.lock(); + +/* following code is faster but unstable since using iterators + while deleting from vector is dangerous and often leads to + undefined results... + + playHandleVector::iterator it = m_playHandles.begin(); + while( it != m_playHandles.end() ) + { + if( ( *it )->done() ) + { + // delete all play-handles which have + // played completely now + delete *it; + m_playHandles.erase( it ); + } + else + { + // play all uncompletely-played play-handles... + ( *it )->play(); + ++it; + } + }*/ + csize idx = 0; + while( idx < m_playHandles.size() ) + { + register playHandle * n = m_playHandles[idx]; + if( n->done() ) + { + // delete all play-handles which have + // played completely now + delete n; + m_playHandles.erase( m_playHandles.begin() + + idx ); + } + else + { + // play all uncompletely-played play-handles... + n->play(); + ++idx; + } + } + + songEditor::inst()->processNextBuffer(); + + // check for samples-packets that have to be mixed in + // the current audio-buffer + for( int i = 0; i < MAX_SAMPLE_PACKETS; ++i ) + { + if( m_samplePackets[i].m_state == samplePacket::READY ) + { + if( m_samplePackets[i].m_framesAhead <= + m_framesPerAudioBuffer ) + { + // found one! mix it... + mixSamplePacket( &m_samplePackets[i] ); + // now this audio-sample can be used + // again + bufferAllocator::free( + m_samplePackets[i].m_buffer ); + m_samplePackets[i].m_state = + samplePacket::UNUSED; + } + else + { + m_samplePackets[i].m_framesAhead -= + m_framesPerAudioBuffer; + } + + } + + } + + if( !m_discardCurBuf ) + { + m_devMutex.lock(); + // write actual data to our current output-device + // (blocking!) + m_audioDev->writeBuffer( m_curBuf, + m_framesPerAudioBuffer, + SAMPLE_RATES[m_qualityLevel], + m_masterOutput ); + m_devMutex.unlock(); + } + else + { + m_discardCurBuf = FALSE; + } + + emit nextAudioBuffer( m_curBuf, m_framesPerAudioBuffer ); + + + m_safetySyncMutex.unlock(); + + + // clear last audio-buffer + clearAudioBuffer( m_curBuf, m_framesPerAudioBuffer ); + + // now swap the buffers... current buffer becomes next (last) + // buffer and the next buffer becomes current (first) buffer + qSwap( m_curBuf, m_nextBuf ); + + // and trigger LFOs + envelopeAndLFOWidget::triggerLFO(); + } + +} + + + + +// removes all play-handles. this is neccessary, when the song is stopped -> +// all remaining notes etc. would be played until their end +void mixer::clear( void ) +{ + m_midiDev->noteOffAll(); + for( playHandleVector::iterator it = m_playHandles.begin(); + it != m_playHandles.end(); ++it ) + { + m_playHandlesToRemove.push_back( *it ); + } +} + + + + +void FASTCALL mixer::clearAudioBuffer( sampleFrame * _ab, Uint32 _frames ) +{ + if( _frames == m_framesPerAudioBuffer ) + { + memcpy( _ab, m_silence, m_framesPerAudioBuffer * + BYTES_PER_FRAME ); + } + else + { + for( Uint32 frame = 0; frame < _frames; ++frame ) + { + for( Uint8 ch = 0; ch < DEFAULT_CHANNELS; ++ch ) + { + _ab[frame][ch] = 0.0f; + } + } + } +} + + + +#ifndef DISABLE_SURROUND +void FASTCALL mixer::clearAudioBuffer( surroundSampleFrame * _ab, + Uint32 _frames ) +{ + if( _frames == m_framesPerAudioBuffer ) + { + memcpy( _ab, m_surroundSilence, m_framesPerAudioBuffer * + BYTES_PER_SURROUND_FRAME ); + } + else + { + for( Uint32 frame = 0; frame < _frames; ++frame ) + { + for( Uint8 ch = 0; ch < DEFAULT_CHANNELS; ++ch ) + { + _ab[frame][ch] = 0.0f; + } + } + } +} +#endif + + + +void FASTCALL mixer::addBuffer( sampleFrame * _buf, Uint32 _frames, + Uint32 _frames_ahead, + volumeVector & _volume_vector ) +{ +#ifdef LMMS_DEBUG + bool success = FALSE; +#endif + for ( Uint16 i = 0; i < MAX_SAMPLE_PACKETS; ++i ) + { + if( m_samplePackets[i].m_state == samplePacket::UNUSED ) + { + m_samplePackets[i].m_state = samplePacket::FILLING; + m_samplePackets[i].m_frames = _frames;//m_framesPerAudioBuffer; + m_samplePackets[i].m_framesDone = 0; + m_samplePackets[i].m_framesAhead = _frames_ahead; + + m_samplePackets[i].m_buffer = + bufferAllocator::alloc( + m_framesPerAudioBuffer ); + // now we have to make a surround-buffer out of a + // stereo-buffer (could be done more easily if there + // would be no volume-vector...) + for( Uint32 frame = 0; frame < _frames/*m_framesPerAudioBuffer*/; + ++frame ) + { + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; + ++chnl ) + { + m_samplePackets[i].m_buffer[frame][chnl] = + _buf[frame][chnl%DEFAULT_CHANNELS] * + _volume_vector.vol[chnl]; + } + } + + m_samplePackets[i].m_state = samplePacket::READY; +#ifdef LMMS_DEBUG + success = TRUE; +#endif + break; + } + } +#ifdef LMMS_DEBUG + if( success == FALSE ) + { + qWarning( "No sample-packets left in mixer::addBuffer(...)!\n" ); + } +#endif +} + + + + +void mixer::setHighQuality( bool _hq_on ) +{ + m_safetySyncMutex.lock(); + + // delete (= close) our audio-device + delete m_audioDev; + + // set new quality-level... + if( _hq_on == TRUE ) + { + m_qualityLevel = HIGH_QUALITY_LEVEL; + } + else + { + m_qualityLevel = DEFAULT_QUALITY_LEVEL; + } + // and re-open device + m_audioDev = tryAudioDevices(); + + m_safetySyncMutex.unlock(); + + emit( sampleRateChanged() ); + +} + + + + +void FASTCALL mixer::setAudioDevice( audioDevice * _dev, bool _hq ) +{ + + m_devMutex.lock(); + + m_oldAudioDev = m_audioDev; + + if( _dev == NULL ) + { + printf( "param _dev == NULL in mixer::setAudioDevice(...). " + "Trying any working audio-device\n" ); + m_audioDev = tryAudioDevices(); + } + else + { + m_audioDev = _dev; + } + + m_qualityLevel = _hq ? 1 : 0; + emit sampleRateChanged(); + + m_devMutex.unlock(); + +} + + + + +void mixer::restoreAudioDevice( void ) +{ + m_devMutex.lock(); + + if( m_oldAudioDev != NULL ) + { + delete m_audioDev; + m_audioDev = m_oldAudioDev; + for( Uint8 qli = 0; qli < QUALITY_LEVELS; ++qli ) + { + if( SAMPLE_RATES[qli] == m_audioDev->sampleRate() ) + { + m_qualityLevel = qli; + emit sampleRateChanged(); + break; + } + } + m_oldAudioDev = NULL; + m_discardCurBuf = TRUE; + } + + m_devMutex.unlock(); +} + + + + +void mixer::checkValidityOfPlayHandles( void ) +{ + playHandleVector::iterator it = m_playHandles.begin(); + while( it != m_playHandles.end() ) + { + ( *it )->checkValidity(); + ++it; + } +} + + + + +void FASTCALL mixer::mixSamplePacket( samplePacket * _sp ) +{ + Uint32 start_frame = _sp->m_framesAhead % m_framesPerAudioBuffer; + Uint32 end_frame = start_frame + _sp->m_frames;//m_framesPerAudioBuffer; + + if( end_frame <= m_framesPerAudioBuffer ) + { + for( Uint32 frame = start_frame; frame < end_frame; ++frame ) + { + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; ++chnl ) + { + m_curBuf[frame][chnl] += + _sp->m_buffer[frame-start_frame][chnl]; + } + } + } + else + { + for( Uint32 frame = start_frame; frame < + m_framesPerAudioBuffer; ++frame ) + { + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; ++chnl ) + { + m_curBuf[frame][chnl] += + _sp->m_buffer[frame-start_frame][chnl]; + } + } + + Uint32 frames_done = m_framesPerAudioBuffer - start_frame; + end_frame = tMin( end_frame -= m_framesPerAudioBuffer, + m_framesPerAudioBuffer ); + + for( Uint32 frame = 0; frame < end_frame; ++frame ) + { + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; ++chnl ) + { + m_nextBuf[frame][chnl] += + _sp->m_buffer[frames_done+frame][chnl]; + } + } + } +} + + + + +audioDevice * mixer::tryAudioDevices( void ) +{ + //m_discardCurBuf = TRUE; + + bool success_ful = FALSE; + audioDevice * dev = NULL; + QString dev_name = configManager::inst()->value( "mixer", "audiodev" ); + +#ifdef OSS_SUPPORT + if( dev_name == audioOSS::name() || dev_name == "" ) + { + dev = new audioOSS( SAMPLE_RATES[DEFAULT_QUALITY_LEVEL], + success_ful ); + if( success_ful ) + { + m_audioDevName = audioOSS::name(); + return( dev ); + } + delete dev; + } +#endif + +#ifdef ALSA_SUPPORT + if( dev_name == audioALSA::name() || dev_name == "" ) + { + dev = new audioALSA( SAMPLE_RATES[DEFAULT_QUALITY_LEVEL], + success_ful ); + if( success_ful ) + { + m_audioDevName = audioALSA::name(); + return( dev ); + } + delete dev; + } +#endif + + +#ifdef JACK_SUPPORT + if( dev_name == audioJACK::name() || dev_name == "" ) + { + dev = new audioJACK( SAMPLE_RATES[DEFAULT_QUALITY_LEVEL], + success_ful ); + if( success_ful ) + { + m_audioDevName = audioJACK::name(); + return( dev ); + } + delete dev; + } +#endif + + +#ifdef SDL_AUDIO_SUPPORT + if( dev_name == audioSDL::name() || dev_name == "" ) + { + dev = new audioSDL( SAMPLE_RATES[DEFAULT_QUALITY_LEVEL], + success_ful ); + if( success_ful ) + { + m_audioDevName = audioSDL::name(); + return( dev ); + } + delete dev; + } +#endif + + // add more device-classes here... + //dev = new audioXXXX( SAMPLE_RATES[m_qualityLevel], success_ful ); + //if( sucess_ful ) + //{ + // return( dev ); + //} + //delete dev + + printf( "No audio-driver working - falling back to dummy-audio-" + "driver\nYou can render your songs and listen to the output " + "files...\n" ); + + m_audioDevName = audioDummy::name(); + + return( new audioDummy( SAMPLE_RATES[m_qualityLevel], success_ful ) ); +} + + + + +midiDevice * mixer::tryMIDIDevices( void ) +{ + QString dev_name = configManager::inst()->value( "mixer", "mididev" ); + +#ifdef ALSA_SUPPORT + if( dev_name == midiALSARaw::name() || dev_name == "" ) + { + midiALSARaw * malsa = new midiALSARaw(); + if( malsa->isRunning() ) + { + m_midiDevName = midiALSARaw::name(); + return( malsa ); + } + delete malsa; + } +#endif + +#ifdef OSS_SUPPORT + if( dev_name == midiOSS::name() || dev_name == "" ) + { + midiOSS * moss = new midiOSS(); + if( moss->isRunning() ) + { + m_midiDevName = midiOSS::name(); + return( moss ); + } + delete moss; + } +#endif + + printf( "Couldn't open a MIDI-device, neither with ALSA nor with " + "OSS. Will use dummy-MIDI-device.\n" ); + + m_midiDevName = midiDummy::name(); + + return( new midiDummy() ); +} + + + +#include "mixer.moc" + diff --git a/src/core/name_label.cpp b/src/core/name_label.cpp new file mode 100644 index 000000000..a2565b444 --- /dev/null +++ b/src/core/name_label.cpp @@ -0,0 +1,153 @@ +/* + * name_label.cpp - implementation of class nameLabel, a label which + * is renamable by double-clicking it + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include + +#endif + + +#include "name_label.h" +#include "rename_dialog.h" +#include "bb_editor.h" +#include "bb_track.h" +#include "templates.h" + +#include "name_label.moc" + + +nameLabel::nameLabel( const QString & _initial_name, QWidget * _parent, + const QPixmap & _pm ) : + QLabel( _initial_name, _parent ), + m_pm( _pm ) +{ +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif +} + + + +nameLabel::~nameLabel() +{ +} + + + + +const QPixmap * nameLabel::pixmap( void ) const +{ + return( &m_pm ); +} + + + + +void nameLabel::setPixmap( const QPixmap & _pm ) +{ + m_pm = _pm; +} + + + + +void nameLabel::rename( void ) +{ + QString txt = text(); + renameDialog rename_dlg( txt ); + rename_dlg.exec(); + if( txt != text() ) + { + setText( txt ); + emit nameChanged( txt ); + } +} + + + + +void nameLabel::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.setFont( pointSize<8>( p.font() ) ); + + QFontMetrics fm( font() ); + + p.drawPixmap( 4, ( height() - m_pm.height() ) / 2, m_pm ); + p.setPen( QColor( 0, 224, 0 ) ); + bbTrack * bbt = bbTrack::findBBTrack( bbEditor::inst()->currentBB() ); + if( bbt != NULL && bbt->getTrackSettingsWidget() == + dynamic_cast( parentWidget() ) ) + { + p.setPen( QColor( 255, 255, 255 ) ); + } + p.drawText( m_pm.width() + 8, height() / 2 + fm.height() / 2 - 4, + text() ); + +#ifndef QT4 + // and blit all the drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + + +void nameLabel::mousePressEvent( QMouseEvent * _me ) +{ + + if( _me->button() == Qt::RightButton ) + { + rename(); + } + else + { + emit clicked(); + //QLabel::mousePressEvent( _me ); + } +} + + + + +void nameLabel::mouseDoubleClickEvent( QMouseEvent * _me ) +{ + rename(); +} diff --git a/src/core/note.cpp b/src/core/note.cpp new file mode 100644 index 000000000..b04d10692 --- /dev/null +++ b/src/core/note.cpp @@ -0,0 +1,189 @@ +/* + * note.cpp - implementation of class note + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "debug.h" +#include "note.h" +#include "templates.h" + + + +note::note( const midiTime & _length, const midiTime & _pos, tones _tone, + octaves _octave, volume _volume, panning _panning ) : + m_tone( C ), + m_octave( DEFAULT_OCTAVE ), + m_volume( DEFAULT_VOLUME ), + m_panning( DEFAULT_PANNING ), + m_length( _length ), + m_pos( _pos ) +{ + setTone( _tone ); + setOctave( _octave ); + setVolume( _volume ); + setPanning( _panning ); +} + + + + +note::~note() +{ +} + + + + +void note::setLength( const midiTime & _length ) +{ + m_length = _length; +} + + + + +void note::setPos( const midiTime & _pos ) +{ + m_pos = _pos; +} + + + + +void note::setTone( tones _tone ) +{ + if( _tone >= C && _tone <= H ) + { + m_tone = _tone; + } + else + { + m_tone = tLimit( _tone, C, H ); +#ifdef LMMS_DEBUG + printf ( "Tone out of range (note::setTone)\n" ); +#endif + } +} + + + + +void note::setOctave( octaves _octave ) +{ + if( _octave >= MIN_OCTAVE && _octave <= MAX_OCTAVE ) + { + m_octave = _octave; + } + else + { + m_octave = tLimit( _octave, MIN_OCTAVE, MAX_OCTAVE ); +#ifdef LMMS_DEBUG + printf( "Octave out of range (note::setOctave)\n" ); +#endif + } +} + + + + +void note::setKey( int _key ) +{ + setTone( static_cast( _key % NOTES_PER_OCTAVE ) ); + setOctave( static_cast( _key / NOTES_PER_OCTAVE ) ); +} + + + + +void note::setVolume( volume _volume ) +{ + if( _volume <= MAX_VOLUME ) + { + m_volume = _volume; + } + else + { + m_volume = tMin( _volume, MAX_VOLUME ); +#ifdef LMMS_DEBUG + printf( "Volume out of range (note::setVolume)\n" ); +#endif + } +} + + + + +void note::setPanning( panning _panning ) +{ + if( _panning >= PANNING_LEFT && _panning <= PANNING_RIGHT ) + { + m_panning = _panning; + } +#ifdef LMMS_DEBUG + else + { + printf ("Paning out of range (note::set_panning)\n"); + } +#endif +} + + + + +void note::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + QDomElement note_de = _doc.createElement( "note" ); + note_de.setAttribute( "tone", QString::number( m_tone ) ); + note_de.setAttribute( "oct", QString::number( m_octave ) ); + note_de.setAttribute( "vol", QString::number( m_volume ) ); + note_de.setAttribute( "pan", QString::number( m_panning ) ); + note_de.setAttribute( "len", QString::number( m_length ) ); + note_de.setAttribute( "pos", QString::number( m_pos ) ); + _parent.appendChild( note_de ); +} + + + + +void note::loadSettings( const QDomElement & _this ) +{ + m_tone = static_cast( _this.attribute( "tone" ).toInt() ); + m_octave = static_cast( _this.attribute( "oct" ).toInt() ); + m_volume = _this.attribute( "vol" ).toInt(); + m_panning = _this.attribute( "pan" ).toInt(); + m_length = _this.attribute( "len" ).toInt(); + m_pos = _this.attribute( "pos" ).toInt(); +} + diff --git a/src/core/note_play_handle.cpp b/src/core/note_play_handle.cpp new file mode 100644 index 000000000..4387bfe1f --- /dev/null +++ b/src/core/note_play_handle.cpp @@ -0,0 +1,259 @@ +/* + * note_play_handle.cpp - implementation of class notePlayHandle, part of + * play-engine + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include + +#include "note_play_handle.h" +#include "channel_track.h" +#include "envelope_tab_widget.h" + + +notePlayHandle::notePlayHandle( channelTrack * _chnl_trk, Uint32 _frames_ahead, + Uint32 _frames, note * n, bool _arp_note ) : + playHandle(), + note( *n ), + m_pluginData( NULL ), + m_filter( NULL ), + m_channelTrack( _chnl_trk ), + m_frames( 0 ), + m_framesAhead( _frames_ahead ), + m_totalFramesPlayed( 0 ), + m_framesBeforeRelease( 0 ), + m_releaseFramesToDo( 0 ), + m_releaseFramesDone( 0 ), + m_released( FALSE ), + m_baseNote( TRUE ), + m_arpNote( _arp_note ), + m_muted( FALSE ) +{ + setFrames( _frames ); +} + + + + +notePlayHandle::~notePlayHandle() +{ + if( m_channelTrack != NULL ) + { + m_channelTrack->deleteNotePluginData( this ); + } + + for( notePlayHandleVector::iterator it = m_subNotes.begin(); + it != m_subNotes.end(); ++it ) + { + delete *it; + } + m_subNotes.clear(); + + delete m_filter; +} + + + + +void notePlayHandle::play( void ) +{ + if( m_muted == TRUE || m_channelTrack == NULL ) + { + return; + } + + if( m_released == FALSE && + m_totalFramesPlayed + mixer::inst()->framesPerAudioBuffer() >= + m_frames ) + { + noteOff( m_frames - m_totalFramesPlayed ); + } + + // play note! + m_channelTrack->playNote( this ); + + if( m_released == TRUE ) + { + Uint32 todo = mixer::inst()->framesPerAudioBuffer(); + // if this note is base-note for arpeggio, always set + // m_releaseFramesToDo to bigger value than m_releaseFramesDone + // because we do not allow notePlayHandle::done() to be true + // until all sub-notes are completely played and no new ones + // are inserted by arpAndChordsTabWidget::processNote() + if( arpBaseNote() == TRUE ) + { + m_releaseFramesToDo = m_releaseFramesDone + 2 * + mixer::inst()->framesPerAudioBuffer(); + } + // look whether we have frames left to be done before release + if( m_framesBeforeRelease ) + { + // yes, then look whether these samples can be played + // within one audio-buffer + if( m_framesBeforeRelease <= + mixer::inst()->framesPerAudioBuffer() ) + { + // yes, then we did less releaseFramesDone + todo -= m_framesBeforeRelease; + m_framesBeforeRelease = 0; + } + else + { + // no, then just decrese framesBeforeRelease + // and wait for next loop... (we're not in + // release-phase yet) + todo = 0; + m_framesBeforeRelease -= + mixer::inst()->framesPerAudioBuffer(); + } + } + // look whether we're in release-phase + if( todo && m_releaseFramesDone < m_releaseFramesToDo ) + { + // check whether we have to do more frames in current + // loop than left in current loop + if( m_releaseFramesToDo - m_releaseFramesDone >= todo ) + { + // yes, then increase number of release-frames + // done + m_releaseFramesDone += todo; + } + else + { + // no, we did all in this loop! + m_releaseFramesDone = m_releaseFramesToDo; + } + } + } + + // play sub-notes (e.g. chords) + for( notePlayHandleVector::iterator it = m_subNotes.begin(); + it != m_subNotes.end(); ) + { + ( *it )->play(); + if( ( *it )->done() ) + { + delete *it; + m_subNotes.erase( it ); + } + else + { + ++it; + } + } + + // if this note is a base-note and there're no more sub-notes left we + // can set m_releaseFramesDone to m_releaseFramesToDo so that + // notePlayHandle::done() returns true and also this base-note is + // removed from mixer's active note vector + if( arpBaseNote() == TRUE && m_subNotes.size() == 0 ) + { + m_releaseFramesDone = m_releaseFramesToDo; + } + + // update internal data + m_totalFramesPlayed += mixer::inst()->framesPerAudioBuffer(); +} + + + + +void notePlayHandle::checkValidity( void ) +{ + if( m_channelTrack != NULL && + m_channelTrack->trackType() == track::NULL_TRACK ) + { + m_channelTrack = NULL; + } +} + + + + +void notePlayHandle::noteOff( Uint32 _s ) +{ + // first note-off all sub-notes + for( notePlayHandleVector::iterator it = m_subNotes.begin(); + it != m_subNotes.end(); ++it ) + { + ( *it )->noteOff( _s ); + } + + // then set some variables indicating release-state + m_framesBeforeRelease = _s; + if( m_channelTrack != NULL ) + { + m_releaseFramesToDo = + m_channelTrack->m_envWidget->releaseFrames(); + } + else + { + m_releaseFramesToDo = 0; + } + + m_released = TRUE; +} + + + + +Uint32 notePlayHandle::actualReleaseFramesToDo( void ) const +{ + return( ( m_channelTrack != NULL ) ? + m_channelTrack->m_envWidget->releaseFrames() : 0 ); +} + + + + +void notePlayHandle::setFrames( Uint32 _frames ) +{ + m_frames = _frames; + if( m_frames == 0 && m_channelTrack != NULL ) + { + m_frames = m_channelTrack->beatLen( this ); + } +} + + + + +float notePlayHandle::volumeLevel( Uint32 _frame ) +{ + return( ( m_channelTrack != NULL ) ? + m_channelTrack->m_envWidget->volumeLevel( this, _frame ) : 0 ); +} + + + + +void notePlayHandle::mute( void ) +{ + // mute all sub-notes + for( notePlayHandleVector::iterator it = m_subNotes.begin(); + it != m_subNotes.end(); ++it ) + { + ( *it )->mute(); + } + m_muted = TRUE; +} + diff --git a/src/core/piano_roll.cpp b/src/core/piano_roll.cpp new file mode 100644 index 000000000..88e9c3a41 --- /dev/null +++ b/src/core/piano_roll.cpp @@ -0,0 +1,2350 @@ +/* + * piano_roll.cpp - implementation of piano-roll which is used for actual + * writing of melodies + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include + +#else + +#include +#include +#include + +#define setChecked setOn + +#endif + + +#include "piano_roll.h" +#include "song_editor.h" +#include "pattern.h" +#include "embed.h" +#include "crystal_button.h" +#include "pixmap_button.h" +#include "note_play_handle.h" +#include "templates.h" +#include "timeline.h" +#include "channel_track.h" +#include "tooltip.h" + + +extern tones whiteKeys[]; // defined in piano_widget.cpp + + +// some constants... +const int INITIAL_PIANOROLL_WIDTH = 640; +const int INITIAL_PIANOROLL_HEIGHT = 480; + +const int SCROLLBAR_SIZE = 16; +const int PIANO_X = 0; + +const int WHITE_KEY_WIDTH = 64; +const int BLACK_KEY_WIDTH = 41; +const int WHITE_KEY_SMALL_HEIGHT = 18; +const int WHITE_KEY_BIG_HEIGHT = 24; +const int BLACK_KEY_HEIGHT = 16; +const int C_KEY_LABEL_X = WHITE_KEY_WIDTH - 19; +const int KEY_LINE_HEIGHT = 12; +const int OCTAVE_HEIGHT = KEY_LINE_HEIGHT * NOTES_PER_OCTAVE; // = 12 * 12; + +const int PR_BOTTOM_MARGIN = SCROLLBAR_SIZE; +const int PR_TOP_MARGIN = 66; + +// width of area used for resizing (the grip at the end of a note) +const int RESIZE_AREA_WIDTH = 3; + +// width of line for setting volume/panning of note +const int NE_LINE_WIDTH = 3; + +// key where to start +const int INITIAL_START_KEY = C + OCTAVE_3 * NOTES_PER_OCTAVE; + + +// init static members of pianoRoll +pianoRoll * pianoRoll::s_instanceOfMe = NULL; + +QPixmap * pianoRoll::s_whiteKeySmallPm = NULL; +QPixmap * pianoRoll::s_whiteKeyBigPm = NULL; +QPixmap * pianoRoll::s_blackKeyPm = NULL; +QPixmap * pianoRoll::s_artwork1 = NULL; +QPixmap * pianoRoll::s_artwork2 = NULL; +QPixmap * pianoRoll::s_toolDraw = NULL; +QPixmap * pianoRoll::s_toolErase = NULL; +QPixmap * pianoRoll::s_toolSelect = NULL; +QPixmap * pianoRoll::s_toolMove = NULL; + +// used for drawing of piano +pianoRoll::pianoRollKeyTypes pianoRoll::prKeyOrder[] = +{ + PR_WHITE_KEY_SMALL, PR_BLACK_KEY, PR_WHITE_KEY_BIG, PR_BLACK_KEY, + PR_WHITE_KEY_SMALL, PR_WHITE_KEY_SMALL, PR_BLACK_KEY, PR_WHITE_KEY_BIG, + PR_BLACK_KEY, PR_WHITE_KEY_BIG, PR_BLACK_KEY, PR_WHITE_KEY_SMALL +} ; + + + +pianoRoll::pianoRoll( void ) : + QWidget( lmmsMainWin::inst()->workspace() ), + m_pattern( NULL ), + m_currentPosition(), + m_recording( FALSE ), + m_currentNote( NULL ), + m_action( NONE ), + m_moveStartKey( 0 ), + m_moveStartTact64th( 0 ), + m_notesEditHeight( 100 ), + m_ppt( KEY_LINE_HEIGHT * MAX_BEATS_PER_TACT ), + m_lenOfNewNotes( midiTime( 0, 16 ) ), + m_shiftPressed( FALSE ), + m_controlPressed( FALSE ), + m_startKey( INITIAL_START_KEY ), + m_lastKey( 0 ), + m_editMode( DRAW ), + m_scrollBack( FALSE ) +{ + // init pixmaps + if( s_whiteKeySmallPm == NULL ) + { + s_whiteKeySmallPm = new QPixmap( embed::getIconPixmap( + "pr_white_key_small" ) ); + } + if( s_whiteKeyBigPm == NULL ) + { + s_whiteKeyBigPm = new QPixmap( embed::getIconPixmap( + "pr_white_key_big" ) ); + } + if( s_blackKeyPm == NULL ) + { + s_blackKeyPm = new QPixmap( embed::getIconPixmap( + "pr_black_key" ) ); + } + if( s_artwork1 == NULL ) + { + s_artwork1 = new QPixmap( embed::getIconPixmap( + "pr_artwork1" ) ); + } + if( s_artwork2 == NULL ) + { + s_artwork2 = new QPixmap( embed::getIconPixmap( + "pr_artwork2" ) ); + } + if( s_toolDraw == NULL ) + { + s_toolDraw = new QPixmap( embed::getIconPixmap( + "pr_tool_draw" ) ); + } + if( s_toolErase == NULL ) + { + s_toolErase= new QPixmap( embed::getIconPixmap( + "pr_tool_erase" ) ); + } + if( s_toolSelect == NULL ) + { + s_toolSelect = new QPixmap( embed::getIconPixmap( + "pr_tool_select" ) ); + } + if( s_toolMove == NULL ) + { + s_toolMove = new QPixmap( embed::getIconPixmap( + "pr_tool_move" ) ); + } + +#ifdef QT4 + // add us to workspace + lmmsMainWin::inst()->workspace()->addWindow( this ); +#endif + + // init control-buttons at the top + m_playButton = new pixmapButton( this ); + m_playButton->move( 8, 7 ); + m_playButton->setCheckable( FALSE ); + m_playButton->setActiveGraphic( embed::getIconPixmap( "play" ) ); + m_playButton->setInactiveGraphic( embed::getIconPixmap( "play" ) ); + m_playButton->setBgGraphic( embed::getIconPixmap( "pr_play_ctrl_bg" ) ); + connect( m_playButton, SIGNAL( clicked() ), this, SLOT( play() ) ); + + m_recordButton = new pixmapButton( this ); + m_recordButton->move( 50, 7 ); + m_recordButton->setCheckable( FALSE ); + m_recordButton->setActiveGraphic( embed::getIconPixmap( "record" ) ); + m_recordButton->setInactiveGraphic( embed::getIconPixmap( "record" ) ); + m_recordButton->setBgGraphic( + embed::getIconPixmap( "pr_play_ctrl_bg" ) ); + connect( m_recordButton, SIGNAL( clicked() ), this, SLOT( record() ) ); + + m_stopButton = new pixmapButton( this ); + m_stopButton->move( 92, 7 ); + m_stopButton->setCheckable( FALSE ); + m_stopButton->setActiveGraphic( embed::getIconPixmap( "stop" ) ); + m_stopButton->setInactiveGraphic( embed::getIconPixmap( "stop" ) ); + m_stopButton->setBgGraphic( embed::getIconPixmap( "pr_play_ctrl_bg" ) ); + connect( m_stopButton, SIGNAL( clicked() ), this, SLOT( stop() ) ); + + toolTip::add( m_playButton, + tr( "Play/pause current pattern (Space)" ) ); + toolTip::add( m_recordButton, + tr( "Record notes from MIDI-device to current " + "pattern" ) ); + toolTip::add( m_stopButton, + tr( "Stop playing of current pattern (Space)" ) ); + +#ifdef QT4 + m_playButton->setWhatsThis( +#else + QWhatsThis::add( m_playButton, +#endif + tr( "Click here, if you want to play the current pattern. " + "This is useful while editing it. The pattern is " + "automatically looped when its end is reached." ) ); +#ifdef QT4 + m_recordButton->setWhatsThis( +#else + QWhatsThis::add( m_recordButton, +#endif + tr( "Click here, if you want to record notes from a MIDI-" + "device or the virtual test-piano of the according " + "channel-window to the current pattern. When recording " + "all notes you play will be written to this pattern " + "and you can edit, play etc. them afterwards." ) ); +#ifdef QT4 + m_stopButton->setWhatsThis( +#else + QWhatsThis::add( m_stopButton, +#endif + tr( "Click here, if you want to stop playing of current " + "pattern." ) ); + + + + removeSelection(); + + // init scrollbars + m_leftRightScroll = new QScrollBar( Qt::Horizontal, this ); + m_topBottomScroll = new QScrollBar( Qt::Vertical, this ); + connect( m_leftRightScroll, SIGNAL( valueChanged( int ) ), this, + SLOT( horScrolled( int ) ) ); + connect( m_topBottomScroll, SIGNAL( valueChanged( int ) ), this, + SLOT( verScrolled( int ) ) ); + + // init edit-buttons at the top + m_drawButton = new crystalButton( embed::getIconPixmap( "pr_tool_bg" ), + embed::getIconPixmap( + "pr_tool_draw" ), this ); + m_drawButton->move( 170, 1 ); + m_drawButton->setActiveButtonBg( embed::getIconPixmap( + "pr_tool_bg_inset" ) ); + m_drawButton->setChecked( TRUE ); + + m_eraseButton = new crystalButton( embed::getIconPixmap( "pr_tool_bg" ), + embed::getIconPixmap( + "pr_tool_erase" ), this ); + m_eraseButton->move( 220, 1 ); + m_eraseButton->setActiveButtonBg( embed::getIconPixmap( + "pr_tool_bg_inset" ) ); + m_selectButton = new crystalButton( embed::getIconPixmap( + "pr_tool_bg" ), + embed::getIconPixmap( + "pr_tool_select" ), this ); + m_selectButton->move( 270, 1 ); + m_selectButton->setActiveButtonBg( embed::getIconPixmap( + "pr_tool_bg_inset" ) ); + m_moveButton = new crystalButton( embed::getIconPixmap( "pr_tool_bg" ), + embed::getIconPixmap( + "pr_tool_move" ), this ); + m_moveButton->move( 320, 1 ); + m_moveButton->setActiveButtonBg( embed::getIconPixmap( + "pr_tool_bg_inset" ) ); + + QButtonGroup * tool_button_group = new QButtonGroup( this ); + tool_button_group->addButton( m_drawButton ); + tool_button_group->addButton( m_eraseButton ); + tool_button_group->addButton( m_selectButton ); + tool_button_group->addButton( m_moveButton ); + tool_button_group->setExclusive( TRUE ); +#ifndef QT4 + tool_button_group->hide(); +#endif + + connect( m_drawButton, SIGNAL( toggled( bool ) ), this, + SLOT( drawButtonToggled( bool ) ) ); + connect( m_eraseButton, SIGNAL( toggled( bool ) ), this, + SLOT( eraseButtonToggled( bool ) ) ); + connect( m_selectButton, SIGNAL( toggled( bool ) ), this, + SLOT( selectButtonToggled( bool ) ) ); + connect( m_moveButton, SIGNAL( toggled( bool ) ), this, + SLOT( moveButtonToggled( bool ) ) ); + + toolTip::add( m_drawButton, + tr( "Click if you want to draw, resize or move single " + "notes (= key 'D')" ) ); + toolTip::add( m_eraseButton, + tr( "Click if you want to erase single notes " + "(= key 'E')" ) ); + toolTip::add( m_selectButton, + tr( "Click if you want to select notes (= key 'S')" ) ); + toolTip::add( m_moveButton, + tr( "Click if you want to move selected notes " + "(= key 'M')" ) ); +#ifdef QT4 + m_drawButton->setWhatsThis( +#else + QWhatsThis::add( m_drawButton, +#endif + tr( "If you click here, draw-mode will be activated. In this " + "mode you can add, resize and move single notes. This " + "is the default-mode which is used most of the time. " + "You can also press 'D' on your keyboard to activate " + "this mode." ) ); +#ifdef QT4 + m_eraseButton->setWhatsThis( +#else + QWhatsThis::add( m_eraseButton, +#endif + tr( "If you click here, erase-mode will be activated. In this " + "mode you can erase single notes. You can also press " + "'E' on your keyboard to activate this mode." ) ); +#ifdef QT4 + m_selectButton->setWhatsThis( +#else + QWhatsThis::add( m_selectButton, +#endif + tr( "If you click here, select-mode will be activated. " + "In this mode you can select notes. This is neccessary " + "if you want to cut, copy, paste, delete or move " + "notes. You can also press 'S' on your keyboard to " + "activate this mode." ) ); +#ifdef QT4 + m_moveButton->setWhatsThis( +#else + QWhatsThis::add( m_moveButton, +#endif + tr( "If you click here, move-mode will be activated. In this " + "mode you can move the notes you selected in select-" + "mode. You can also press 'M' on your keyboard to " + "activate this mode." ) ); + + + m_cutButton = new crystalButton( embed::getIconPixmap( "pr_tool_bg" ), + embed::getIconPixmap( + "pr_edit_cut" ), this ); + m_cutButton->move( 390, 1 ); + m_cutButton->setActiveButtonBg( embed::getIconPixmap( + "pr_tool_bg_inset" ) ); + m_cutButton->setCheckable( FALSE ); + m_copyButton = new crystalButton( embed::getIconPixmap( "pr_tool_bg" ), + embed::getIconPixmap( + "pr_edit_copy" ), + this ); + m_copyButton->move( 440, 1 ); + m_copyButton->setActiveButtonBg( embed::getIconPixmap( + "pr_tool_bg_inset" ) ); + m_copyButton->setCheckable( FALSE ); + m_pasteButton = new crystalButton( embed::getIconPixmap( "pr_tool_bg" ), + embed::getIconPixmap( + "pr_edit_paste" ), + this ); + m_pasteButton->move( 490, 1 ); + m_pasteButton->setActiveButtonBg( embed::getIconPixmap( + "pr_tool_bg_inset" ) ); + m_pasteButton->setCheckable( FALSE ); + + connect( m_cutButton, SIGNAL( clicked() ), this, + SLOT( cutSelectedNotes() ) ); + connect( m_copyButton, SIGNAL( clicked() ), this, + SLOT( copySelectedNotes() ) ); + connect( m_pasteButton, SIGNAL( clicked() ), this, + SLOT( pasteNotes() ) ); + + toolTip::add( m_cutButton, tr( "Cut selected notes (Ctrl+X)" ) ); + toolTip::add( m_copyButton, tr( "Copy selected notes (Ctrl+C)" ) ); + toolTip::add( m_pasteButton, tr( "Paste notes from clipboard " + "(Ctrl+V)" ) ); +#ifdef QT4 + m_cutButton->setWhatsThis( +#else + QWhatsThis::add( m_cutButton, +#endif + tr( "If you click here, selected notes will be cut into the " + "clipboard. You can paste them anywhere in any pattern " + "by clicking on the paste-button." ) ); +#ifdef QT4 + m_copyButton->setWhatsThis( +#else + QWhatsThis::add( m_copyButton, +#endif + tr( "If you click here, selected notes will be copied into the " + "clipboard. You can paste them anywhere in any pattern " + "by clicking on the paste-button." ) ); +#ifdef QT4 + m_pasteButton->setWhatsThis( +#else + QWhatsThis::add( m_pasteButton, +#endif + tr( "If you click here, the notes from the clipboard will be " + "pasted at the first visible tact." ) ); + + + // setup our actual window + setWindowIcon( embed::getIconPixmap( "piano" ) ); + resize( INITIAL_PIANOROLL_WIDTH, INITIAL_PIANOROLL_HEIGHT ); + setCurrentPattern( NULL ); + +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + //setMouseTracking( TRUE ); + + hide(); + + // add time-line + m_timeLine = new timeLine( WHITE_KEY_WIDTH, 48, m_ppt, + songEditor::inst()->getPlayPos( + songEditor::PLAY_PATTERN ), + m_currentPosition, this ); + connect( this, SIGNAL( positionChanged( const midiTime & ) ), + m_timeLine, SLOT( updatePosition( const midiTime & ) ) ); + connect( m_timeLine, SIGNAL( positionChanged( const midiTime & ) ), + this, SLOT( updatePosition( const midiTime & ) ) ); +} + + + + +pianoRoll::~pianoRoll() +{ +} + + + + +void pianoRoll::setCurrentPattern( pattern * _new_pattern ) +{ + m_pattern = _new_pattern; + m_currentPosition = 0; + m_startKey = INITIAL_START_KEY; + + if( validPattern() == FALSE ) + { + // we must not call resizeEvent with NULL-pointer when + // being called of of ctor + if( s_instanceOfMe == this ) + { + resizeEvent( NULL ); + } + setWindowTitle( tr( "Piano-Roll - no pattern" ) ); + + update(); + return; + } + + + noteVector & notes = m_pattern->notes(); + int central_key = 0; + if( notes.isEmpty() == FALSE ) + { + // determine the central key so that we can scroll to it + int total_notes = 0; + for( noteVector::iterator it = notes.begin(); + it != notes.end(); ++it ) + { + if( ( *it )->length() > 0 ) + { + central_key += ( *it )->key(); + ++total_notes; + } + } + + if( total_notes > 0 ) + { + central_key = central_key / total_notes - + ( NOTES_PER_OCTAVE * OCTAVES - + m_totalKeysToScroll ) / 2; + m_startKey = tLimit( central_key, 0, + OCTAVES * NOTES_PER_OCTAVE ); + } + } + // resizeEvent() does the rest for us (scrolling, range-checking + // of start-notes and so on...) + resizeEvent( NULL ); + + // remove all connections to other channel-tracks + disconnect( this, SLOT( recordNote( const note & ) ) ); + + // and now connect to noetFromMidiDeviceDone of channel so that + // we receive note-off-events from midi-keyboard for recording it + connect( m_pattern->getChannelTrack(), + SIGNAL( noteFromMidiDeviceDone( const note & ) ), + this, SLOT( recordNote( const note & ) ) ); + + setWindowTitle( tr( "Piano-Roll - %1" ).arg( m_pattern->name() ) ); + + update(); +} + + + + +inline void pianoRoll::drawNoteRect( QPainter & _p, Uint16 _x, Uint16 _y, + Sint16 _width, bool _is_selected ) +{ + ++_x; + ++_y; + _width -= 2; + + if( _width <= 0 ) + { + _width = 2; + } + + QColor current_color( 0xFF, 0xB0, 0x00 ); + if( _is_selected ) + { + current_color.setRgb( 0x00, 0x40, 0xC0 ); + } + _p.fillRect( _x, _y, _width, KEY_LINE_HEIGHT - 2, current_color ); + + _p.setPen( QColor( 0xFF, 0xDF, 0x20 ) ); + _p.drawLine( _x, _y, _x + _width, _y ); + _p.drawLine( _x, _y, _x, _y + KEY_LINE_HEIGHT - 2 ); + + _p.setPen( QColor( 0xFF, 0x9F, 0x00 ) ); + _p.drawLine( _x + _width, _y, _x + _width, _y + KEY_LINE_HEIGHT - 2 ); + _p.drawLine( _x, _y + KEY_LINE_HEIGHT - 2, _x + _width, + _y + KEY_LINE_HEIGHT - 2 ); + + _p.setPen( QColor( 0xFF, 0xFF, 0x40 ) ); + if( _width > 2 ) + { + _p.drawLine( _x + _width - 3, _y + 2, _x + _width - 3, + _y + KEY_LINE_HEIGHT - 4 ); + } + _p.drawLine( _x + _width - 1, _y + 2, _x + _width - 1, + _y + KEY_LINE_HEIGHT - 4 ); + _p.drawLine( _x + _width - 2, _y + 2, _x + _width - 2, + _y + KEY_LINE_HEIGHT - 4 ); +} + + + + +void pianoRoll::removeSelection( void ) +{ + m_selectStartTact64th = 0; + m_selectedTact64th = 0; + m_selectStartKey = 0; + m_selectedKeys = 0; +} + + + + +void pianoRoll::closeEvent( QCloseEvent * _ce ) +{ + QApplication::restoreOverrideCursor(); + hide(); + _ce->ignore (); +} + + + + +void pianoRoll::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); + p.fillRect( rect(), QColor( 0, 0, 0 ) ); +#else + QPixmap draw_pm( rect().size() ); + draw_pm.fill( QColor( 0, 0, 0 ) );//, rect().topLeft()); + + QPainter p( &draw_pm, this ); +#endif + + // set font-size to 8 + p.setFont( pointSize<8>( p.font() ) ); + + // y_offset is used to align the piano-keys on the key-lines + int y_offset = 0; + + // calculate y_offset according to first key + switch( prKeyOrder[m_startKey % NOTES_PER_OCTAVE] ) + { + case PR_BLACK_KEY: y_offset = KEY_LINE_HEIGHT/4; break; + case PR_WHITE_KEY_BIG: y_offset = KEY_LINE_HEIGHT/2; break; + case PR_WHITE_KEY_SMALL: + if( prKeyOrder[( ( m_startKey + 1 ) % + NOTES_PER_OCTAVE)] != PR_BLACK_KEY ) + { + y_offset = KEY_LINE_HEIGHT / 2; + } + break; + } + + // start drawing at the bottom + int key_line_y = height() - PR_BOTTOM_MARGIN - m_notesEditHeight - 1; + // used for aligning black-keys later + int first_white_key_height = WHITE_KEY_SMALL_HEIGHT; + // key-counter - only needed for finding out whether the processed + // key is the first one + int keys_processed = 0; + + int key = m_startKey; + + // draw all white keys... + for( int y = key_line_y + 1 + y_offset; y > PR_TOP_MARGIN; + key_line_y -= KEY_LINE_HEIGHT, ++keys_processed ) + { + // check for white key that is only half visible on the + // bottom of piano-roll + if( keys_processed == 0 && + prKeyOrder[m_startKey % NOTES_PER_OCTAVE] == + PR_BLACK_KEY ) + { + // draw it! + p.drawPixmap( PIANO_X, y - WHITE_KEY_SMALL_HEIGHT, + *s_whiteKeySmallPm ); + // update y-pos + y -= WHITE_KEY_SMALL_HEIGHT / 2; + // move first black key down (we didn't draw whole + // white key so black key needs to be lifted down) + // (default for first_white_key_height = + // WHITE_KEY_SMALL_HEIGHT, so WHITE_KEY_SMALL_HEIGHT/2 + // is smaller) + first_white_key_height = WHITE_KEY_SMALL_HEIGHT / 2; + } + // check whether to draw a big or a small white key + if( prKeyOrder[key % NOTES_PER_OCTAVE] == PR_WHITE_KEY_SMALL ) + { + // draw a small one... + p.drawPixmap( PIANO_X, y - WHITE_KEY_SMALL_HEIGHT, + *s_whiteKeySmallPm ); + // update y-pos + y -= WHITE_KEY_SMALL_HEIGHT; + + } + else if( prKeyOrder[key % NOTES_PER_OCTAVE] == + PR_WHITE_KEY_BIG ) + { + // draw a big one... + p.drawPixmap( PIANO_X, y-WHITE_KEY_BIG_HEIGHT, + *s_whiteKeyBigPm ); + // if a big white key has been the first key, + // black keys needs to be lifted up + if( keys_processed == 0 ) + { + first_white_key_height = WHITE_KEY_BIG_HEIGHT; + } + // update y-pos + y -= WHITE_KEY_BIG_HEIGHT; + } + // label C-keys... + if( static_cast( key % NOTES_PER_OCTAVE ) == C ) + { + p.setPen( QColor( 240, 240, 240 ) ); + p.drawText( C_KEY_LABEL_X + 1, y+14, "C" + + QString::number( static_cast( key / + NOTES_PER_OCTAVE ) ) ); + p.setPen( QColor( 0, 0, 0 ) ); + p.drawText( C_KEY_LABEL_X, y + 13, "C" + + QString::number( static_cast( key / + NOTES_PER_OCTAVE ) ) ); + p.setPen( QColor( 0x4F, 0x4F, 0x4F ) ); + } + else + { + p.setPen( QColor( 0x3F, 0x3F, 0x3F ) ); + } + // draw key-line + p.drawLine( WHITE_KEY_WIDTH, key_line_y, width(), key_line_y ); + + ++key; + } + + // reset all values, because now we're going to draw all black keys + key = m_startKey; + keys_processed = 0; + int white_cnt = 0; + + // and go! + for( int y = height() - PR_BOTTOM_MARGIN - m_notesEditHeight + y_offset; + y > PR_TOP_MARGIN; ++keys_processed ) + { + // check for black key that is only half visible on the bottom + // of piano-roll + if( keys_processed == 0 + // current key may not be a black one + && prKeyOrder[key % NOTES_PER_OCTAVE] != PR_BLACK_KEY + // but the previous one must be black (we must check this + // because there might be two white keys (E-F) + && prKeyOrder[( key - 1 ) % NOTES_PER_OCTAVE] == + PR_BLACK_KEY ) + { + // draw the black key! + p.drawPixmap( PIANO_X, y - BLACK_KEY_HEIGHT / 2, + *s_blackKeyPm ); + // is the one after the start-note a black key?? + if( prKeyOrder[( key + 1 ) % NOTES_PER_OCTAVE] != + PR_BLACK_KEY ) + { + // no, then move it up! + y -= KEY_LINE_HEIGHT / 2; + } + } + // current key black? + if( prKeyOrder[key % NOTES_PER_OCTAVE] == PR_BLACK_KEY ) + { + // then draw it (calculation of y very complicated, + // but that's the only working solution, sorry...) + p.drawPixmap( PIANO_X, y - ( first_white_key_height - + WHITE_KEY_SMALL_HEIGHT ) - + WHITE_KEY_SMALL_HEIGHT/2 - 1 - + BLACK_KEY_HEIGHT, *s_blackKeyPm ); + + // update y-pos + y -= WHITE_KEY_BIG_HEIGHT; + // reset white-counter + white_cnt = 0; + } + else + { + // simple workaround for increasing x if there were + // two white keys (e.g. between E and F) + ++white_cnt; + if( white_cnt > 1 ) + { + y -= WHITE_KEY_BIG_HEIGHT/2; + } + } + + ++key; + } + + + // erase the area below the piano, because there might be keys that + // should be only half-visible + p.fillRect( QRect( 0, height() - PR_BOTTOM_MARGIN - m_notesEditHeight, + WHITE_KEY_WIDTH, m_notesEditHeight ), + QColor( 0, 0, 0 ) ); + + + // draw artwork-stuff + p.drawPixmap( 0, 0, *s_artwork1 ); + + int artwork_x = s_artwork1->width(); + + while( artwork_x < width() ) + { + p.drawPixmap( artwork_x, 0, *s_artwork2 ); + artwork_x += s_artwork2->width(); + } + + + // set clipping area, because we may not draw on keyboard... + p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, width()-WHITE_KEY_WIDTH, + height()-PR_TOP_MARGIN-PR_BOTTOM_MARGIN ); + + // draw vertical raster + int tact_16th = m_currentPosition / 4; + int offset = ( m_currentPosition % 4 ) * m_ppt / + MAX_BEATS_PER_TACT / 4; + for( int x = WHITE_KEY_WIDTH - offset; x < width(); + x += m_ppt/MAX_BEATS_PER_TACT, ++tact_16th ) + { + if( x >= WHITE_KEY_WIDTH ) + { + // every tact-start needs to be a bright line + if( tact_16th % 16 == 0 ) + { + p.setPen( QColor( 0x7F, 0x7F, 0x7F ) ); + } + // normal line + else if( tact_16th % 4 == 0 ) + { + p.setPen( QColor( 0x5F, 0x5F, 0x5F ) ); + } + // weak line + else + { + p.setPen( QColor( 0x3F, 0x3F, 0x3F ) ); + } + p.drawLine( x, PR_TOP_MARGIN, x, height() - + PR_BOTTOM_MARGIN ); + } + } + + + + // following code draws all notes in visible area + volume-lines + + + p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, width() - + WHITE_KEY_WIDTH, height() - PR_TOP_MARGIN - + PR_BOTTOM_MARGIN ); + + // setup selection-vars + int sel_pos_start = m_selectStartTact64th; + int sel_pos_end = m_selectStartTact64th+m_selectedTact64th; + if( sel_pos_start > sel_pos_end ) + { + qSwap( sel_pos_start, sel_pos_end ); + } + + int sel_key_start = m_selectStartKey - m_startKey + 1; + int sel_key_end = sel_key_start + m_selectedKeys; + if( sel_key_start > sel_key_end ) + { + qSwap( sel_key_start, sel_key_end ); + } + + int y_base = height() - PR_BOTTOM_MARGIN - m_notesEditHeight - 1; + if( validPattern() == TRUE ) + { + noteVector & notes = m_pattern->notes(); + + const int visible_keys = ( height() - PR_TOP_MARGIN - + PR_BOTTOM_MARGIN - m_notesEditHeight ) / + KEY_LINE_HEIGHT + 2; + + for( noteVector::iterator it = notes.begin(); it != notes.end(); + ++it ) + { + Sint32 len_tact_64th = ( *it )->length(); + + if( len_tact_64th <= 0 ) + { + continue; + } + const int key = ( *it )->key() - m_startKey + 1; + + Sint32 pos_tact_64th = ( *it )->pos(); + + int note_width = len_tact_64th * m_ppt / 64; + const int x = ( pos_tact_64th - m_currentPosition ) * + m_ppt / 64; + // skip this note if not in visible area at all + if( !( x + note_width >= 0 && + x <= width() - WHITE_KEY_WIDTH ) ) + { + continue; + } + + // is the note in visible area? + if( key > 0 && key <= visible_keys ) + { + bool is_selected = FALSE; + // if we're in move-mode, we may only draw notes + // in selected area, that have originally been + // selected and not notes that are now in + // selection because the user moved it... + if( m_editMode == MOVE ) + { + if( qFind( m_selNotesForMove.begin(), + m_selNotesForMove.end(), + *it ) != + m_selNotesForMove.end() ) + { + is_selected = TRUE; + } + } + else if( key > sel_key_start && + key <= sel_key_end && + pos_tact_64th >= sel_pos_start && + pos_tact_64th + len_tact_64th <= + sel_pos_end ) + { + is_selected = TRUE; + } + + // we've done and checked all, lets draw the + // note + drawNoteRect( p, x + WHITE_KEY_WIDTH, + y_base - key * KEY_LINE_HEIGHT, + note_width, + is_selected ); + } + // draw volume-line of note + p.setPen( QPen( QColor( 0, 255, 0 ), NE_LINE_WIDTH ) ); + p.drawLine( x + WHITE_KEY_WIDTH + 1, + height() - PR_BOTTOM_MARGIN - + ( *it )->getVolume() / 2, + x + WHITE_KEY_WIDTH + 1, + height() - PR_BOTTOM_MARGIN ); + } + } + else + { + QFont f = p.font(); + f.setBold( TRUE ); + p.setFont( pointSize<14>( f ) ); + p.setPen( QColor( 0, 255, 0 ) ); + p.drawText( WHITE_KEY_WIDTH + 20, PR_TOP_MARGIN + 40, + tr( "Please open a pattern by double-clicking " + "on it!" ) ); + } + + p.setClipRect( WHITE_KEY_WIDTH, PR_TOP_MARGIN, width() - + WHITE_KEY_WIDTH, height() - PR_TOP_MARGIN - + m_notesEditHeight - PR_BOTTOM_MARGIN ); + + // now draw selection-frame + int x = ( ( sel_pos_start - m_currentPosition ) * m_ppt ) / 64; + int w = ( ( ( sel_pos_end - m_currentPosition ) * m_ppt ) / + 64 ) - x; + int y = (int) y_base - sel_key_start * KEY_LINE_HEIGHT; + int h = (int) y_base - sel_key_end * KEY_LINE_HEIGHT - y; + p.setPen( QColor( 0, 64, 192 ) ); + p.drawRect( x + WHITE_KEY_WIDTH, y, w, h ); + + int l = ( validPattern() == TRUE )? (int) m_pattern->length() : 0; + + // reset scroll-range + m_leftRightScroll->setRange( 0, l ); +#ifdef QT4 + m_leftRightScroll->setSingleStep( 1 ); + m_leftRightScroll->setPageStep( l ); +#else + m_leftRightScroll->setSteps( 1, l ); +#endif +/* + // draw current edit-mode-icon below the cursor + switch( m_editMode ) + { + case DRAW: + p.drawPixmap( mapFromGlobal( QCursor::pos() ), + *s_toolDraw ); + break; + case ERASE: + p.drawPixmap( mapFromGlobal( QCursor::pos() ), + *s_toolErase ); + break; + case SELECT: + p.drawPixmap( mapFromGlobal( QCursor::pos() ), + *s_toolSelect ); + break; + case MOVE: + p.drawPixmap( mapFromGlobal( QCursor::pos() ), + *s_toolMove ); + break; + }*/ + +#ifndef QT4 + // and blit all the drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + + +// responsible for moving/resizing scrollbars after window-resizing +void pianoRoll::resizeEvent( QResizeEvent * ) +{ + + m_leftRightScroll->setGeometry( WHITE_KEY_WIDTH, height() - + SCROLLBAR_SIZE, + width()-WHITE_KEY_WIDTH, + SCROLLBAR_SIZE ); + m_topBottomScroll->setGeometry( width() - SCROLLBAR_SIZE, PR_TOP_MARGIN, + SCROLLBAR_SIZE, + height() - PR_TOP_MARGIN - + SCROLLBAR_SIZE ); + + int total_pixels = OCTAVE_HEIGHT * OCTAVES - ( height() - + PR_TOP_MARGIN - PR_BOTTOM_MARGIN - + m_notesEditHeight ); + m_totalKeysToScroll = total_pixels * NOTES_PER_OCTAVE / OCTAVE_HEIGHT; + + m_topBottomScroll->setRange( 0, m_totalKeysToScroll ); +#ifdef QT4 + m_topBottomScroll->setSingleStep( 1 ); + m_topBottomScroll->setPageStep( 20 ); +#else + m_topBottomScroll->setSteps( 1, 20 ); +#endif + + if( m_startKey > m_totalKeysToScroll ) + { + m_startKey = m_totalKeysToScroll; + } + m_topBottomScroll->setValue( m_totalKeysToScroll - m_startKey ); + + songEditor::inst()->getPlayPos( songEditor::PLAY_PATTERN + ).m_timeLine->setFixedWidth( width() ); +} + + + + +int pianoRoll::getKey( int _y ) +{ + int key_line_y = height() - PR_BOTTOM_MARGIN - m_notesEditHeight - 1; + // pressed key on piano + int key_num = ( key_line_y - _y ) / KEY_LINE_HEIGHT; + key_num += m_startKey; + + // some range-checking-stuff + if( key_num < 0 ) + { + key_num = 0; + } + + if( key_num >= NOTES_PER_OCTAVE * OCTAVES ) + { + key_num = NOTES_PER_OCTAVE * OCTAVES - 1; + } + + return( m_lastKey = key_num ); +} + + + + +void pianoRoll::mousePressEvent( QMouseEvent * _me ) +{ + if( validPattern() == FALSE ) + { + return; + } + + if( _me->y() > PR_TOP_MARGIN ) + { + bool play_note = TRUE; + volume vol = DEFAULT_VOLUME; + + bool edit_note = ( _me->y() > height() - + PR_BOTTOM_MARGIN - m_notesEditHeight ); + + int key_num = getKey( _me->y() ); + + int x = _me->x(); + + if( x > WHITE_KEY_WIDTH ) + { + // set, move or resize note + + x -= WHITE_KEY_WIDTH; + + // get tact-64th in which the user clicked + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + + // get note-vector of current pattern + noteVector & notes = m_pattern->notes(); + + // will be our iterator in the following loop + noteVector::iterator it = notes.begin(); + + // loop through whole note-vector... + while( it != notes.end() ) + { + // and check whether the user clicked on an + // existing note or an edit-line + if( pos_tact_64th >= ( *it )->pos() && + ( *it )->length() > 0 && + ( + ( edit_note == FALSE && + pos_tact_64th <= ( *it )->pos() + + ( *it )->length() && + ( *it )->key() == key_num ) + || + ( edit_note == TRUE && + pos_tact_64th <= ( *it )->pos() + + NE_LINE_WIDTH * 64 / + m_ppt ) + ) + ) + { + break; + } + ++it; + } + + // first check whether the user clicked in note-edit- + // area + if( edit_note == TRUE ) + { + if( it != notes.end() ) + { + vol = 2 * ( -_me->y() + height() - + PR_BOTTOM_MARGIN ); + ( *it )->setVolume( vol ); + m_currentNote = *it; + m_action = CHANGE_NOTE_VOLUME; + key_num = ( *it )->key(); + } + else + { + play_note = FALSE; + } + } + // left button?? + else if( _me->button() == Qt::LeftButton && + m_editMode == DRAW ) + { + // did it reach end of vector because + // there's no note?? + if( it == notes.end() ) + { + m_pattern->setType( + pattern::MELODY_PATTERN ); + + // then set new note + midiTime note_pos( pos_tact_64th ); + midiTime note_len( m_lenOfNewNotes ); + + note new_note( note_len, note_pos, + (tones)( key_num % + NOTES_PER_OCTAVE ), + (octaves)( key_num / + NOTES_PER_OCTAVE) ); + + note * created_new_note = + m_pattern->addNote( new_note ); + + // reset it so that it can be used for + // ops (move, resize) after this + // code-block + it = notes.begin(); + while( it != notes.end() && + *it != created_new_note ) + { + ++it; + } + } + + m_currentNote = *it; + + // clicked at the "tail" of the note? + if( pos_tact_64th > m_currentNote->pos() + + m_currentNote->length() - + RESIZE_AREA_WIDTH ) + { + // then resize the note + m_action = RESIZE_NOTE; + + // set resize-cursor + QCursor c( Qt::SizeHorCursor ); + QApplication::setOverrideCursor( c ); + play_note = FALSE; + } + else + { + // otherwise move it + m_action = MOVE_NOTE; + int aligned_x = (int)( (float)( ( + m_currentNote->pos() - + m_currentPosition ) * + m_ppt ) / 64.0f ); + m_moveXOffset = x - aligned_x - 1; + // set move-cursor + QCursor c( Qt::SizeAllCursor ); + QApplication::setOverrideCursor( c ); + } + + songEditor::inst()->setModified(); + } + else if( ( _me->button() == Qt::RightButton && + m_editMode == DRAW ) || + m_editMode == ERASE ) + { + // erase single note + + play_note = FALSE; + if( it != notes.end() ) + { + m_pattern->removeNote( *it ); + } + + songEditor::inst()->setModified(); + } + else if( _me->button() == Qt::LeftButton && + m_editMode == SELECT ) + { + + // select an area of notes + + m_selectStartTact64th = pos_tact_64th; + m_selectedTact64th = 0; + m_selectStartKey = key_num; + m_selectedKeys = 1; + m_action = SELECT_NOTES; + + play_note = FALSE; + } + else if( _me->button() == Qt::RightButton && + m_editMode == SELECT ) + { + // when clicking right in select-move, we + // switch to move-mode + m_moveButton->setChecked( TRUE ); + play_note = FALSE; + + } + else if( _me->button() == Qt::LeftButton && + m_editMode == MOVE ) + { + + // move selection (including selected notes) + + // save position where move-process began + m_moveStartTact64th = pos_tact_64th; + m_moveStartKey = key_num; + + m_action = MOVE_SELECTION; + + play_note = FALSE; + songEditor::inst()->setModified(); + } + else if( _me->button() == Qt::RightButton && + m_editMode == MOVE ) + { + // when clicking right in select-move, we + // switch to draw-mode + m_drawButton->setChecked( TRUE ); + play_note = FALSE; + } + + update(); + } + + // was there an action where should be played the note? + if( play_note == TRUE && m_recording == FALSE && + songEditor::inst()->playing() == FALSE ) + { + m_pattern->getChannelTrack()->processInEvent( + midiEvent( NOTE_ON, 0, key_num, vol ) ); + } + } +} + + + + +void pianoRoll::mouseReleaseEvent( QMouseEvent * _me ) +{ + if( validPattern() == TRUE ) + { + if( m_action == CHANGE_NOTE_VOLUME && m_currentNote != NULL ) + { + m_pattern->getChannelTrack()->processInEvent( + midiEvent( NOTE_OFF, 0, + m_currentNote->key() ) ); + } + else + { + m_pattern->getChannelTrack()->processInEvent( + midiEvent( NOTE_OFF, 0, getKey( _me->y() ) ) ); + } + } + + m_currentNote = NULL; + + m_action = NONE; + + if( m_editMode == DRAW ) + { + QApplication::restoreOverrideCursor(); + } +} + + + + +void pianoRoll::mouseMoveEvent( QMouseEvent * _me ) +{ + if( validPattern() == FALSE ) + { + update(); + return; + } + + // save current last-key-var + int released_key = m_lastKey; + + if( _me->y() > PR_TOP_MARGIN ) + { + bool edit_note = ( _me->y() > height() - + PR_BOTTOM_MARGIN - m_notesEditHeight ); + + int key_num = getKey( _me->y() ); + int x = _me->x(); + + // is the calculated key different from current key? + // (could be the user just moved the cursor one pixel up/down + // but is still on the same key) + if( key_num != released_key && + m_action != CHANGE_NOTE_VOLUME && + edit_note == FALSE ) + { + m_pattern->getChannelTrack()->processInEvent( + midiEvent( NOTE_OFF, 0, released_key ) ); + if( +#ifdef QT4 + _me->buttons() & +#else + _me->modifiers() == +#endif + Qt::LeftButton && + m_action != RESIZE_NOTE && + m_action != SELECT_NOTES && + m_recording == FALSE && + songEditor::inst()->playing() == FALSE ) + { + m_pattern->getChannelTrack()->processInEvent( + midiEvent( NOTE_ON, 0, key_num, + DEFAULT_VOLUME ) ); + } + } + if( _me->x() <= WHITE_KEY_WIDTH ) + { + update(); + return; + } + x -= WHITE_KEY_WIDTH; + + if( edit_note == TRUE || m_action == CHANGE_NOTE_VOLUME ) + { + if( m_action == CHANGE_NOTE_VOLUME && + m_currentNote != NULL ) + { + volume vol = tLimit( 2 * ( -_me->y() + + height() - + PR_BOTTOM_MARGIN ), + MIN_VOLUME, + MAX_VOLUME ); + m_currentNote->setVolume( vol ); + if( m_pattern->getChannelTrack()->keyPressed( + m_currentNote->key() ) == TRUE ) + { + m_pattern->getChannelTrack()->noteForKey( m_currentNote->key() + )->setVolume( vol ); + } + } + } + else if( m_currentNote != NULL && +#ifdef QT4 + _me->buttons() & +#else + _me->state() == +#endif + Qt::LeftButton && m_editMode == DRAW ) + { + int key_num = getKey( _me->y() ); + + if( m_action == MOVE_NOTE ) + { + x -= m_moveXOffset; + } + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + if( m_action == MOVE_NOTE ) + { + // moving note + if( pos_tact_64th < 0 ) + { + pos_tact_64th = 0; + } + m_currentNote->setPos( midiTime( + pos_tact_64th ) ); + m_currentNote->setKey( key_num ); + + // we moved the note so the note has to be + // moved properly according to new starting- + // time in the note-array of pattern + m_currentNote = m_pattern->rearrangeNote( + m_currentNote ); + } + else + { + // resizing note + int tact_64th_diff = pos_tact_64th - + m_currentNote->pos(); + if( tact_64th_diff <= 0 ) + { + tact_64th_diff = 1; + } + m_lenOfNewNotes = midiTime( tact_64th_diff ); + m_currentNote->setLength( m_lenOfNewNotes ); + m_pattern->update(); + } + + songEditor::inst()->setModified(); + + } + else if( _me->button() == Qt::NoButton && m_editMode == DRAW ) + { + // set move- or resize-cursor + + // get tact-64th in which the cursor is posated + int pos_tact_64th = ( x * 64 ) / m_ppt + + m_currentPosition; + + // get note-vector of current pattern + noteVector & notes = m_pattern->notes(); + + // will be our iterator in the following loop + noteVector::iterator it = notes.begin(); + + // loop through whole note-vector... + while( it != notes.end() ) + { + // and check whether the cursor is over an + // existing note + if( pos_tact_64th >= ( *it )->pos() && + pos_tact_64th <= ( *it )->pos() + + ( *it )->length() && + ( *it )->key() == key_num && + (*it )->length() > 0 ) + { + break; + } + ++it; + } + + // did it reach end of vector because there's + // no note?? + if( it != notes.end() ) + { + // cursor at the "tail" of the note? + if( pos_tact_64th > ( *it )->pos() + + ( *it )->length() - + RESIZE_AREA_WIDTH ) + { + if( QApplication::overrideCursor() ) + { + if( QApplication::overrideCursor()->shape() != Qt::SizeHorCursor ) + { + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + + QCursor c( Qt::SizeHorCursor ); + QApplication::setOverrideCursor( c ); + } + } + else + { + QCursor c( Qt::SizeHorCursor ); + QApplication::setOverrideCursor( + c ); + } + } + else + { + if( QApplication::overrideCursor() ) + { + if( QApplication::overrideCursor()->shape() != Qt::SizeAllCursor ) + { + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + + QCursor c( Qt::SizeAllCursor ); + QApplication::setOverrideCursor( + c ); + } + } + else + { + QCursor c( Qt::SizeAllCursor ); + QApplication::setOverrideCursor( + c ); + } + } + } + else + { + // the cursor is over no note, so restore cursor + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + } + } + else if( +#ifdef QT4 + _me->buttons() & +#else + _me->modifiers() == +#endif + Qt::LeftButton && + m_editMode == SELECT && + m_action == SELECT_NOTES ) + { + + // change size of selection + + if( x < 0 && m_currentPosition > 0 ) + { + x = 0; + QCursor::setPos( mapToGlobal( QPoint( + WHITE_KEY_WIDTH, + _me->y() ) ) ); + if( m_currentPosition >= 4 ) + { + m_leftRightScroll->setValue( + m_currentPosition - 4 ); + } + else + { + m_leftRightScroll->setValue( 0 ); + } + } + else if( x > width() - WHITE_KEY_WIDTH ) + { + x = width() - WHITE_KEY_WIDTH; + QCursor::setPos( mapToGlobal( QPoint( width(), + _me->y() ) ) ); + m_leftRightScroll->setValue( m_currentPosition + + 4 ); + } + + // get tact-64th in which the cursor is posated + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + + m_selectedTact64th = pos_tact_64th - + m_selectStartTact64th; + if( (int) m_selectStartTact64th + m_selectedTact64th < + 0 ) + { + m_selectedTact64th = -static_cast( + m_selectStartTact64th ); + } + m_selectedKeys = key_num - m_selectStartKey; + if( key_num <= m_selectStartKey ) + { + --m_selectedKeys; + } + } + else if( +#ifdef QT4 + _me->buttons() & +#else + _me->modifiers() == +#endif + Qt::LeftButton && + m_editMode == MOVE && + m_action == MOVE_SELECTION ) + { + // move selection + selected notes + + // do horizontal move-stuff + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + int tact_64th_diff = pos_tact_64th - + m_moveStartTact64th; + if( m_selectedTact64th > 0 ) + { + if( (int) m_selectStartTact64th + + tact_64th_diff < 0 ) + { + tact_64th_diff = -m_selectStartTact64th; + } + } + else + { + if( (int) m_selectStartTact64th + + m_selectedTact64th + tact_64th_diff < + 0 ) + { + tact_64th_diff = -( + m_selectStartTact64th + + m_selectedTact64th ); + } + } + m_selectStartTact64th += tact_64th_diff; + + int tact_diff = tact_64th_diff / 64; + tact_64th_diff = tact_64th_diff % 64; + + + // do vertical move-stuff + int key_diff = key_num - m_moveStartKey; + + if( m_selectedKeys > 0 ) + { + if( m_selectStartKey + key_diff < -1 ) + { + key_diff = -m_selectStartKey - 1; + } + else if( m_selectStartKey + m_selectedKeys + + key_diff >= NOTES_PER_OCTAVE * + OCTAVES ) + { + key_diff = NOTES_PER_OCTAVE * OCTAVES - + ( m_selectStartKey + + m_selectedKeys ) - 1; + } + } + else + { + if( m_selectStartKey + m_selectedKeys + + key_diff < -1 ) + { + key_diff = -( m_selectStartKey + + m_selectedKeys ) - 1; + } + else if( m_selectStartKey + key_diff >= + NOTES_PER_OCTAVE * OCTAVES ) + { + key_diff = NOTES_PER_OCTAVE * OCTAVES - + m_selectStartKey - 1; + } + } + m_selectStartKey += key_diff; + + + for( noteVector::iterator it = + m_selNotesForMove.begin(); + it != m_selNotesForMove.end(); ++it ) + { + int note_tact = ( *it )->pos().getTact() + + tact_diff; + int note_tact_64th = + ( *it )->pos().getTact64th() + + tact_64th_diff; + while( note_tact_64th < 0 ) + { + --note_tact; + note_tact_64th += 64; + } + while( note_tact_64th > 64 ) + { + ++note_tact; + note_tact_64th -= 64; + } + midiTime new_note_pos( note_tact, + note_tact_64th ); + ( *it )->setPos( new_note_pos ); + ( *it )->setKey( ( *it )->key() + key_diff ); + *it = m_pattern->rearrangeNote( *it ); + } + + m_moveStartTact64th = pos_tact_64th; + m_moveStartKey = key_num; + } + } + else + { + if( +#ifdef QT4 + _me->buttons() & +#else + _me->modifiers() == +#endif + Qt::LeftButton && + m_editMode == SELECT && + m_action == SELECT_NOTES ) + { + + int x = _me->x() - WHITE_KEY_WIDTH; + if( x < 0 && m_currentPosition > 0 ) + { + x = 0; + QCursor::setPos( mapToGlobal( QPoint( + WHITE_KEY_WIDTH, + _me->y() ) ) ); + if( m_currentPosition >= 4 ) + { + m_leftRightScroll->setValue( + m_currentPosition - 4 ); + } + else + { + m_leftRightScroll->setValue( 0 ); + } + } + else if( x > width() - WHITE_KEY_WIDTH ) + { + x = width() - WHITE_KEY_WIDTH; + QCursor::setPos( mapToGlobal( QPoint( width(), + _me->y() ) ) ); + m_leftRightScroll->setValue( m_currentPosition + + 4 ); + } + + // get tact-64th in which the cursor is posated + int pos_tact_64th = x * 64 / m_ppt + + m_currentPosition; + + m_selectedTact64th = pos_tact_64th - + m_selectStartTact64th; + if( (int) m_selectStartTact64th + m_selectedTact64th < + 0 ) + { + m_selectedTact64th = -static_cast( + m_selectStartTact64th ); + } + + + int key_num = getKey( _me->y() ); + int visible_keys = ( height() - PR_TOP_MARGIN - + PR_BOTTOM_MARGIN - + m_notesEditHeight ) / + KEY_LINE_HEIGHT + 2; + const int s_key = m_startKey - 1; + + if( key_num <= s_key ) + { + QCursor::setPos( mapToGlobal( QPoint( _me->x(), + height() - + PR_BOTTOM_MARGIN - + m_notesEditHeight ) ) ); + m_topBottomScroll->setValue( + m_topBottomScroll->value() + 1 ); + key_num = s_key; + } + else if( key_num >= s_key + visible_keys ) + { + QCursor::setPos( mapToGlobal( QPoint( _me->x(), + PR_TOP_MARGIN ) ) ); + m_topBottomScroll->setValue( + m_topBottomScroll->value() - 1 ); + key_num = s_key + visible_keys; + } + + m_selectedKeys = key_num - m_selectStartKey; + if( key_num <= m_selectStartKey ) + { + --m_selectedKeys; + } + } + QApplication::restoreOverrideCursor(); + } + + update(); +} + + + + +void pianoRoll::keyPressEvent( QKeyEvent * _ke ) +{ + if( _ke->key() == Qt::Key_Shift ) + { + m_shiftPressed = TRUE; + } + else + { + m_shiftPressed = FALSE; + } + if( _ke->key() == Qt::Key_Control ) + { + m_controlPressed = TRUE; + } + else + { + m_controlPressed = FALSE; + } + + switch( _ke->key() ) + { + case Qt::Key_Up: + m_topBottomScroll->setValue( + m_topBottomScroll->value() - 1 ); + break; + + case Qt::Key_Down: + m_topBottomScroll->setValue( + m_topBottomScroll->value() + 1 ); + break; + + case Qt::Key_Left: + { + if( ( m_timeLine->pos() -= 16 ) < 0 ) + { + m_timeLine->pos() = 0; + } + m_timeLine->updatePosition(); + break; + } + case Qt::Key_Right: + { + m_timeLine->pos() += 16; + m_timeLine->updatePosition(); + break; + } + + case Qt::Key_C: + if( _ke->modifiers() & Qt::ControlModifier ) + { + copySelectedNotes(); + } + else + { + _ke->ignore(); + } + break; + + case Qt::Key_X: + if( _ke->modifiers() & Qt::ControlModifier ) + { + cutSelectedNotes(); + } + else + { + _ke->ignore(); + } + break; + + case Qt::Key_V: + if( _ke->modifiers() & Qt::ControlModifier ) + { + pasteNotes(); + } + else + { + _ke->ignore(); + } + break; + + case Qt::Key_A: + if( _ke->modifiers() & Qt::ControlModifier ) + { + m_selectButton->setChecked( TRUE ); + selectAll(); + update(); + } + else + { + _ke->ignore(); + } + break; + + case Qt::Key_D: + m_drawButton->setChecked( TRUE ); + break; + + case Qt::Key_E: + m_eraseButton->setChecked( TRUE ); + break; + + case Qt::Key_S: + m_selectButton->setChecked( TRUE ); + break; + + case Qt::Key_M: + m_moveButton->setChecked( TRUE ); + break; + + case Qt::Key_Delete: + deleteSelectedNotes(); + break; + + case Qt::Key_Space: + if( songEditor::inst()->playing() ) + { + stop(); + } + else + { + play(); + } + break; + + case Qt::Key_Home: + m_timeLine->pos() = 0; + m_timeLine->updatePosition(); + break; + + default: + _ke->ignore(); + break; + } +} + + + + +void pianoRoll::keyReleaseEvent( QKeyEvent * ) +{ + m_shiftPressed = FALSE; + m_controlPressed = FALSE; +} + + + + +void pianoRoll::wheelEvent( QWheelEvent * _we ) +{ + if( m_controlPressed ) + { + if( _we->delta() > 0 ) + { + m_ppt = tMin( m_ppt * 2, KEY_LINE_HEIGHT * + MAX_BEATS_PER_TACT * 8 ); + } + else if( m_ppt >= 72 ) + { + m_ppt /= 2; + } + m_timeLine->setPixelsPerTact( m_ppt ); + update(); + } + else if( m_shiftPressed ) + { + m_leftRightScroll->setValue( m_leftRightScroll->value() - + _we->delta() * 2 / 15 ); + } + else + { + m_topBottomScroll->setValue( m_topBottomScroll->value() - + _we->delta() / 30 ); + } +} + + + + +void pianoRoll::play( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + if( songEditor::inst()->playing() ) + { + if( songEditor::inst()->playMode() != songEditor::PLAY_PATTERN ) + { + songEditor::inst()->stop(); + songEditor::inst()->playPattern( m_pattern ); + m_playButton->setInactiveGraphic( + embed::getIconPixmap( "pause" ) ); + } + else + { + songEditor::inst()->pause(); + m_playButton->setInactiveGraphic( + embed::getIconPixmap( "play" ) ); + } + } + else if( songEditor::inst()->paused() ) + { + songEditor::inst()->resumeFromPause(); + m_playButton->setInactiveGraphic( + embed::getIconPixmap( "pause" ) ); + } + else + { + m_playButton->setInactiveGraphic( + embed::getIconPixmap( "pause" ) ); + songEditor::inst()->playPattern( m_pattern ); + } +} + + + + +void pianoRoll::record( void ) +{ + if( songEditor::inst()->playing() ) + { + stop(); + } + if( m_recording == TRUE || validPattern() == FALSE ) + { + return; + } + + m_recording = TRUE; + songEditor::inst()->playPattern( m_pattern, FALSE ); +} + + + + +void pianoRoll::stop( void ) +{ + songEditor::inst()->stop(); + m_playButton->setInactiveGraphic( embed::getIconPixmap( "play" ) ); + m_playButton->update(); + m_recording = FALSE; + m_scrollBack = TRUE; +} + + + + +void pianoRoll::recordNote( const note & _n ) +{ + if( m_recording == TRUE && validPattern() == TRUE ) + { + note n( _n ); + n.setPos( songEditor::inst()->getPlayPos( + songEditor::PLAY_PATTERN ) - n.length() ); + m_pattern->addNote( n ); + update(); + songEditor::inst()->setModified(); + } +} + + + + +void pianoRoll::horScrolled( int _new_pos ) +{ + m_currentPosition = _new_pos; + emit positionChanged( m_currentPosition ); + update(); +} + + + + +void pianoRoll::verScrolled( int _new_pos ) +{ + // revert value + m_startKey = m_totalKeysToScroll - _new_pos; + + update(); +} + + + + +void pianoRoll::drawButtonToggled( bool _on ) +{ + if( _on ) + { + m_editMode = DRAW; + removeSelection(); + update(); + } +} + + + +void pianoRoll::eraseButtonToggled( bool _on ) +{ + if( _on ) + { + m_editMode = ERASE; + removeSelection(); + update(); + } +} + + + + +void pianoRoll::selectButtonToggled( bool _on ) +{ + if( _on ) + { + m_editMode = SELECT; + removeSelection(); + update(); + } +} + + + +void pianoRoll::moveButtonToggled( bool _on ) +{ + if( _on ) + { + m_editMode = MOVE; + m_selNotesForMove.clear(); + getSelectedNotes( m_selNotesForMove ); + update(); + } +} + + + +void pianoRoll::selectAll( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + noteVector & notes = m_pattern->notes(); + + // if first_time = TRUE, we HAVE to set the vars for select + bool first_time = TRUE; + + for( noteVector::iterator it = notes.begin(); it != notes.end(); ++it ) + { + Uint32 len_tact_64th = ( *it )->length(); + + if( len_tact_64th > 0 ) + { + const int key = ( *it )->key(); + + Uint32 pos_tact_64th = ( *it )->pos(); + if( key <= m_selectStartKey || first_time ) + { + // if we move start-key down, we have to add + // the difference between old and new start-key + // to m_selectedKeys, otherwise the selection + // is just moved down... + int diff = m_selectStartKey - ( key - 1 ); + m_selectStartKey = key - 1; + m_selectedKeys += diff; + } + if( key >= m_selectedKeys+m_selectStartKey || + first_time ) + { + m_selectedKeys = key - m_selectStartKey; + } + if( pos_tact_64th < m_selectStartTact64th || + first_time ) + { + m_selectStartTact64th = pos_tact_64th; + } + if( pos_tact_64th + len_tact_64th > + m_selectStartTact64th + m_selectedTact64th || + first_time ) + { + m_selectedTact64th = pos_tact_64th + + len_tact_64th - + m_selectStartTact64th; + } + first_time = FALSE; + } + } +} + + + + +// returns vector with pointers to all selected notes +void pianoRoll::getSelectedNotes( noteVector & _selected_notes ) +{ + if( validPattern() == FALSE ) + { + return; + } + + int sel_pos_start = m_selectStartTact64th; + int sel_pos_end = sel_pos_start + m_selectedTact64th; + if( sel_pos_start > sel_pos_end ) + { + qSwap( sel_pos_start, sel_pos_end ); + } + + int sel_key_start = m_selectStartKey; + int sel_key_end = sel_key_start + m_selectedKeys; + if( sel_key_start > sel_key_end ) + { + qSwap( sel_key_start, sel_key_end ); + } + + noteVector & notes = m_pattern->notes(); + + for( noteVector::iterator it = notes.begin(); it != notes.end(); ++it ) + { + Sint32 len_tact_64th = ( *it )->length(); + + if( len_tact_64th > 0 ) + { + int key = ( *it )->key(); + Sint32 pos_tact_64th = ( *it )->pos(); + + if( key > sel_key_start && + key <= sel_key_end && + pos_tact_64th >= sel_pos_start && + pos_tact_64th+len_tact_64th <= sel_pos_end ) + { + _selected_notes.push_back( *it ); + } + } + } +} + + + + +void pianoRoll::copySelectedNotes( void ) +{ + for( noteVector::iterator it = m_notesToCopy.begin(); + it != m_notesToCopy.end(); ++it ) + { + delete *it; + } + + m_notesToCopy.clear(); + + noteVector selected_notes; + getSelectedNotes( selected_notes ); + + if( selected_notes.isEmpty() == FALSE ) + { + midiTime start_pos( selected_notes.first()->pos().getTact(), + 0 ); + for( noteVector::iterator it = selected_notes.begin(); + it != selected_notes.end(); ++it ) + { + m_notesToCopy.push_back( new note( **it ) ); + m_notesToCopy.last()->setPos( m_notesToCopy.last()->pos( + start_pos ) ); + } + } +} + + + + +void pianoRoll::cutSelectedNotes( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + for( noteVector::iterator it = m_notesToCopy.begin(); + it != m_notesToCopy.end(); ++it ) + { + delete *it; + } + + m_notesToCopy.clear(); + + noteVector selected_notes; + getSelectedNotes( selected_notes ); + + if( selected_notes.isEmpty() == FALSE ) + { + songEditor::inst()->setModified(); + + midiTime start_pos( selected_notes.first()->pos().getTact(), + 0 ); + + while( selected_notes.isEmpty() == FALSE ) + { + note * new_note = new note( *selected_notes.first() ); + new_note->setPos( new_note->pos( start_pos ) ); + m_notesToCopy.push_back( new_note ); + + // note (the memory of it) is also deleted by + // pattern::removeNote(...) so we don't have to do that + m_pattern->removeNote( selected_notes.first() ); + selected_notes.erase( selected_notes.begin() ); + } + } + + update(); + songEditor::inst()->update(); +} + + + + +void pianoRoll::pasteNotes( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + if( m_notesToCopy.isEmpty() == FALSE ) + { + for( noteVector::iterator it = m_notesToCopy.begin(); + it != m_notesToCopy.end(); ++it ) + { + note cur_note( **it ); + cur_note.setPos( cur_note.pos() + m_currentPosition ); + m_pattern->addNote( cur_note ); + } + + // we only have to do the following lines if we pasted at + // least one note... + songEditor::inst()->setModified(); + update(); + songEditor::inst()->update (); + } +} + + + + +void pianoRoll::deleteSelectedNotes( void ) +{ + if( validPattern() == FALSE ) + { + return; + } + + noteVector selected_notes; + getSelectedNotes( selected_notes ); + + const bool update_after_delete = !selected_notes.isEmpty(); + + while( selected_notes.isEmpty() == FALSE ) + { + m_pattern->removeNote( selected_notes.first() ); + selected_notes.erase( selected_notes.begin() ); + } + + if( update_after_delete ) + { + songEditor::inst()->setModified(); + update(); + songEditor::inst()->update(); + } +} + + + + +void pianoRoll::updatePosition( const midiTime & _t ) +{ + if( ( songEditor::inst()->playing() && + songEditor::inst()->playMode() == songEditor::PLAY_PATTERN ) || + m_scrollBack == TRUE ) + { + const int w = width() - WHITE_KEY_WIDTH; + if( _t > m_currentPosition + w * 64 / m_ppt ) + { + m_leftRightScroll->setValue( _t.getTact() * 64 ); + } + else if( _t < m_currentPosition ) + { + midiTime t = tMax( _t - w * 64 * 64 / m_ppt, 0 ); + m_leftRightScroll->setValue( t.getTact() * 64 ); + } + m_scrollBack = FALSE; + } +} + + + +#undef setChecked + + +#include "piano_roll.moc" + diff --git a/src/core/piano_widget.cpp b/src/core/piano_widget.cpp new file mode 100644 index 000000000..5d319cf12 --- /dev/null +++ b/src/core/piano_widget.cpp @@ -0,0 +1,619 @@ +/* + * piano_widget.cpp - implementation of piano-widget used in channel-window + * for testing channel + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include + +#endif + + +#include "piano_widget.h" +#include "channel_track.h" +#include "note_play_handle.h" +#include "embed.h" + + +const keyTypes KEY_ORDER[] = +{ + // C CIS D DIS E F + WHITE_KEY, BLACK_KEY, WHITE_KEY, BLACK_KEY, WHITE_KEY, WHITE_KEY, + // FIS G GIS A B H + BLACK_KEY, WHITE_KEY, BLACK_KEY, WHITE_KEY, BLACK_KEY, WHITE_KEY +} ; + + +tones WHITE_KEYS[] = +{ + C, D, E, F, G, A, H +} ; + + +QPixmap * pianoWidget::s_whiteKeyPm = NULL; +QPixmap * pianoWidget::s_blackKeyPm = NULL; +QPixmap * pianoWidget::s_whiteKeyPressedPm = NULL; +QPixmap * pianoWidget::s_blackKeyPressedPm = NULL; + + +const int PIANO_BASE = 11; +const int WHITE_KEY_WIDTH = 10; +const int BLACK_KEY_WIDTH = 8; +const int WHITE_KEY_HEIGHT = 57; +const int BLACK_KEY_HEIGHT = 38; +const int LABEL_TEXT_SIZE = 7; + + +pianoWidget::pianoWidget (channelTrack * _parent ) : + QWidget( _parent ), + m_channelTrack( _parent ), + m_startTone( C ), + m_startOctave( OCTAVE_3 ) +{ +#ifdef QT4 + setFocusPolicy( Qt::StrongFocus ); +#else + setFocusPolicy( StrongFocus ); +#endif + + if( s_whiteKeyPm == NULL ) + { + s_whiteKeyPm = new QPixmap( embed::getIconPixmap( + "white_key" ) ); + } + if( s_blackKeyPm == NULL ) + { + s_blackKeyPm = new QPixmap( embed::getIconPixmap( + "black_key" ) ); + } + if( s_whiteKeyPressedPm == NULL ) + { + s_whiteKeyPressedPm = new QPixmap( embed::getIconPixmap( + "white_key_pressed" ) ); + } + if ( s_blackKeyPressedPm == NULL ) + { + s_blackKeyPressedPm = new QPixmap( embed::getIconPixmap( + "black_key_pressed" ) ); + } + +#ifdef QT4 + m_pianoScroll = new QScrollBar( Qt::Horizontal, this ); + m_pianoScroll->setRange( 0, WHITE_KEYS_PER_OCTAVE * ( OCTAVES - 3 ) - + 4 ); + m_pianoScroll->setSingleStep( 1 ); + m_pianoScroll->setPageStep( 20 ); + m_pianoScroll->setValue( OCTAVE_3*WHITE_KEYS_PER_OCTAVE ); +#else + // create scrollbar below piano-widget... + m_pianoScroll = new QScrollBar( 0, WHITE_KEYS_PER_OCTAVE * + ( OCTAVES - 3 ) - 4, 1, 20, + OCTAVE_3 * WHITE_KEYS_PER_OCTAVE, + Qt::Horizontal, this ); +#endif + m_pianoScroll->setGeometry( 0, PIANO_BASE+WHITE_KEY_HEIGHT, 250, 16 ); + // ...and connect it to this widget... + connect( m_pianoScroll, SIGNAL( valueChanged( int ) ), this, + SLOT( pianoScrolled( int ) ) ); + +#ifndef QT4 + // set background-mode for flicker-free redraw + setBackgroundMode( Qt::NoBackground ); +#endif +} + + + + +pianoWidget::~pianoWidget() +{ +} + + + + +// gets the key from the given mouse-position +int pianoWidget::getKeyFromMouse( const QPoint & _p ) +{ + + int key_num = (int)( (float) _p.x() / (float) WHITE_KEY_WIDTH ); + + for( int i = 0; i <= key_num; ++i ) + { + if( KEY_ORDER[( m_startOctave * NOTES_PER_OCTAVE + + m_startTone +i ) % NOTES_PER_OCTAVE] == + BLACK_KEY ) + { + ++key_num; + } + } + + key_num += m_startOctave * NOTES_PER_OCTAVE + m_startTone; + + // is it a black key? + if( _p.y() < PIANO_BASE + BLACK_KEY_HEIGHT ) + { + // then do extra checking whether the mouse-cursor is over + // a black key + if( key_num > 0 && + KEY_ORDER[( key_num - 1 ) % NOTES_PER_OCTAVE] == + BLACK_KEY && + _p.x() % WHITE_KEY_WIDTH <= ( WHITE_KEY_WIDTH / 2 ) - + ( BLACK_KEY_WIDTH / 2 ) ) + { + --key_num; + } + if( key_num < NOTES_PER_OCTAVE * OCTAVES - 1 && + KEY_ORDER[( key_num + 1 ) % NOTES_PER_OCTAVE] == + BLACK_KEY && + _p.x() % WHITE_KEY_WIDTH >= + ( WHITE_KEY_WIDTH - BLACK_KEY_WIDTH / 2 ) ) + { + ++key_num; + } + } + + // some range-checking-stuff + key_num = tLimit( key_num, 0, NOTES_PER_OCTAVE * OCTAVES - 1 ); + + return( m_lastKey = key_num ); +} + + + + +// handler for scrolling-event +void pianoWidget::pianoScrolled( int _new_pos ) +{ + m_startTone = WHITE_KEYS[_new_pos % WHITE_KEYS_PER_OCTAVE]; + m_startOctave = (octaves)( _new_pos / WHITE_KEYS_PER_OCTAVE ); + + update(); +} + + + + +// handler for mouse-click-event +void pianoWidget::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton ) + { + // get pressed key + int key_num = getKeyFromMouse( _me->pos() ); + if( _me->pos().y() > PIANO_BASE ) + { + int y_diff = _me->pos().y() - PIANO_BASE; + volume vol = (volume)( ( float ) y_diff / + ( ( KEY_ORDER[key_num % NOTES_PER_OCTAVE] == + WHITE_KEY ) ? + WHITE_KEY_HEIGHT : BLACK_KEY_HEIGHT ) * + (float) DEFAULT_VOLUME); + if( y_diff < 0 ) + { + vol = 0; + } + else if( y_diff > ( ( KEY_ORDER[key_num % + NOTES_PER_OCTAVE] == + WHITE_KEY ) ? + WHITE_KEY_HEIGHT : BLACK_KEY_HEIGHT ) ) + { + vol = DEFAULT_VOLUME; + } + // set note on + m_channelTrack->processInEvent( + midiEvent( NOTE_ON, 0, key_num, vol ) ); + } + else + { + m_channelTrack->setBaseTone( static_cast( + key_num % NOTES_PER_OCTAVE ) ); + m_channelTrack->setBaseOctave( static_cast( + key_num / NOTES_PER_OCTAVE ) ); + } + + // and let the user see that he pressed a key... :) + update(); + } +} + + + + +// handler for mouse-release-event +void pianoWidget::mouseReleaseEvent( QMouseEvent * _me ) +{ + int released_key = getKeyFromMouse( _me->pos() ); + + m_channelTrack->processInEvent( + midiEvent ( NOTE_OFF, 0, released_key ) ); + + // and let the user see that he released a key... :) + update(); +} + + + + +// handler for mouse-move-event +void pianoWidget::mouseMoveEvent( QMouseEvent * _me ) +{ + // save current last-key-var + int released_key = m_lastKey; + int key_num = getKeyFromMouse( _me->pos() ); + int y_diff = _me->pos().y() - PIANO_BASE; + volume vol = (volume)( (float) y_diff / + ( ( KEY_ORDER[key_num % NOTES_PER_OCTAVE] == WHITE_KEY ) ? + WHITE_KEY_HEIGHT : BLACK_KEY_HEIGHT ) * + (float)DEFAULT_VOLUME ); + // maybe the user moved the mouse-cursor above or under the + // piano-widget while holding left button so check that and + // correct volume if necessary + if( y_diff < 0 ) + { + vol = 0; + } + else if( y_diff > + ( ( KEY_ORDER[key_num % NOTES_PER_OCTAVE] == WHITE_KEY ) ? + WHITE_KEY_HEIGHT : BLACK_KEY_HEIGHT ) ) + { + vol = DEFAULT_VOLUME; + } + + // is the calculated key different from current key? (could be the + // user just moved the cursor one pixel left but on the same key) + if( key_num != released_key ) + { + m_channelTrack->processInEvent( + midiEvent( NOTE_OFF, 0, released_key ) ); +#ifdef QT4 + if( _me->buttons() & Qt::LeftButton ) +#else + if( _me->state() == Qt::LeftButton ) +#endif + { + if( _me->pos().y() > PIANO_BASE ) + { + m_channelTrack->processInEvent( + midiEvent( NOTE_ON, 0, key_num, vol ) ); + } + else + { + m_channelTrack->setBaseTone( (tones) + ( key_num % NOTES_PER_OCTAVE ) ); + m_channelTrack->setBaseOctave( (octaves) + ( key_num / NOTES_PER_OCTAVE ) ); + } + } + // and let the user see that he pressed a key... :) + update(); + } + else if( m_channelTrack->keyPressed( key_num ) == TRUE ) + { + m_channelTrack->noteForKey( key_num )->setVolume( vol ); + } + +} + + + + +int pianoWidget::getKeyFromKeyboard( int _k ) const +{ + switch( _k ) + { + case Qt::Key_Y: return( 0 ); + case Qt::Key_S: return( 1 ); + case Qt::Key_X: return( 2 ); + case Qt::Key_D: return( 3 ); + case Qt::Key_C: return( 4 ); + case Qt::Key_V: return( 5 ); + case Qt::Key_G: return( 6 ); + case Qt::Key_B: return( 7 ); + case Qt::Key_H: return( 8 ); + case Qt::Key_N: return( 9 ); + case Qt::Key_J: return( 10 ); + case Qt::Key_M: return( 11 ); + case Qt::Key_Q: return( 12 ); + case Qt::Key_2: return( 13 ); + case Qt::Key_W: return( 14 ); + case Qt::Key_3: return( 15 ); + case Qt::Key_E: return( 16 ); + case Qt::Key_R: return( 17 ); + case Qt::Key_5: return( 18 ); + case Qt::Key_T: return( 19 ); + case Qt::Key_6: return( 20 ); + case Qt::Key_Z: return( 21 ); + case Qt::Key_7: return( 22 ); + case Qt::Key_U: return( 23 ); + case Qt::Key_I: return( 24 ); + case Qt::Key_9: return( 25 ); + case Qt::Key_O: return( 26 ); + case Qt::Key_0: return( 27 ); + case Qt::Key_P: return( 28 ); + } + + return( -100 ); +} + + + + +void pianoWidget::keyPressEvent( QKeyEvent * _ke ) +{ + int key_num = getKeyFromKeyboard( _ke->key() ) + + ( DEFAULT_OCTAVE - 1 ) * NOTES_PER_OCTAVE; + + if( _ke->isAutoRepeat() == FALSE && key_num > -1 ) + { + m_channelTrack->processInEvent( + midiEvent( NOTE_ON, 0, key_num, DEFAULT_VOLUME ) ); + update(); + } + else + { + _ke->ignore(); + } +} + + + + +void pianoWidget::keyReleaseEvent( QKeyEvent * _ke ) +{ + int key_num = getKeyFromKeyboard( _ke->key() ) + + ( DEFAULT_OCTAVE - 1 ) * NOTES_PER_OCTAVE; + if( _ke->isAutoRepeat() == FALSE && key_num > -1 ) + { + m_channelTrack->processInEvent( + midiEvent( NOTE_OFF, 0, key_num ) ); + update(); + } + else + { + _ke->ignore(); + } +} + + + + +void pianoWidget::focusInEvent( QFocusEvent * ) +{ + mixer::inst()->getMIDIDevice()->setChannelTrack( m_channelTrack ); +} + + + + +void pianoWidget::focusOutEvent( QFocusEvent * ) +{ + // if we loose focus, we HAVE to note off all "running" notes because + // we don't receive key-release-events anymore and so the notes would + // hang otherwise + for( int i = 0; i < NOTES_PER_OCTAVE * OCTAVES; ++i ) + { + if( m_channelTrack->keyPressed( i ) ) + { + m_channelTrack->processInEvent( + midiEvent( NOTE_OFF, 0, i ) ); + } + } + update(); +} + + + + +int pianoWidget::getKeyX( int _key_num ) +{ + int k = m_startOctave*NOTES_PER_OCTAVE + m_startTone; + if( _key_num < k ) + { + return( ( _key_num - k ) * WHITE_KEY_WIDTH / 2 ); + } + + int x = 0; + int white_cnt = 0; + + while( k <= _key_num ) + { + if( KEY_ORDER[k % NOTES_PER_OCTAVE] == WHITE_KEY ) + { + ++white_cnt; + if( white_cnt > 1 ) + { + x += WHITE_KEY_WIDTH; + } + else + { + x += WHITE_KEY_WIDTH/2; + } + } + else + { + white_cnt = 0; + x += WHITE_KEY_WIDTH/2; + } + ++k; + } + + x -= WHITE_KEY_WIDTH / 2; + + return( x ); + +} + + + + +void pianoWidget::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + // create pixmap for whole widget + QPixmap pm( rect().size() );//width(), PIANO_BASE+WHITE_KEY_HEIGHT); + // and a painter for it + QPainter p( &pm, this ); +#endif + + // set smaller font for printing number of every octave + p.setFont( pointSize( p.font() ) ); + + + // draw blue bar above the actual keyboard (there will be the labels + // for all C's) + p.fillRect( QRect( 0, 1, width(), PIANO_BASE-2 ), + QColor( 0x00, 0x00, 0xFF ) ); + + // draw stuff above the actual keyboard + p.setPen( QColor( 0x00, 0x00, 0x00 ) ); + p.drawLine( 0, 0, width(), 0 ); + p.drawLine( 0, PIANO_BASE-1, width(), PIANO_BASE-1 ); + + p.setPen( QColor ( 0xFF, 0xFF, 0xFF ) ); + + int base_key = m_channelTrack->baseTone() + + m_channelTrack->baseOctave() * NOTES_PER_OCTAVE; + if( KEY_ORDER[base_key % NOTES_PER_OCTAVE] == WHITE_KEY ) + { + p.fillRect( QRect( getKeyX( base_key ), 1, WHITE_KEY_WIDTH-1, + PIANO_BASE-2 ), + QColor( 0xFF, 0xBB, 0x00 ) ); + } + else + { + p.fillRect( QRect( getKeyX( base_key ) + 1, 1, + BLACK_KEY_WIDTH - 1, PIANO_BASE - 2 ), + QColor( 0xFF, 0xBB, 0x00 ) ); + } + + + int cur_key = m_startOctave*NOTES_PER_OCTAVE + m_startTone; + + // draw all white keys... + for( int x = 0; x < width(); ) + { + while( KEY_ORDER[cur_key%NOTES_PER_OCTAVE] != WHITE_KEY ) + { + ++cur_key; + } + + // draw pressed or not pressed key, depending on state of + // current key + if( m_channelTrack->keyPressed( cur_key ) == TRUE ) + { + p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPressedPm ); + } + else + { + p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPm ); + } + + x += WHITE_KEY_WIDTH; + + if( (tones) (cur_key%NOTES_PER_OCTAVE) == C ) + { + // label key of note C with "C" and number of current + // octave + p.drawText( x - WHITE_KEY_WIDTH, LABEL_TEXT_SIZE + 2, + QString( "C" ) + QString::number( + cur_key / NOTES_PER_OCTAVE, 10 ) ); + } + ++cur_key; + } + + + // reset all values, because now we're going to draw all black keys + cur_key = m_startOctave*NOTES_PER_OCTAVE + m_startTone; + int white_cnt = 0; + + int s_key = m_startOctave*NOTES_PER_OCTAVE+m_startTone; + if( s_key > 0 && + KEY_ORDER[(tones)( --s_key ) % NOTES_PER_OCTAVE] == BLACK_KEY ) + { + if( m_channelTrack->keyPressed( s_key ) == TRUE ) + { + p.drawPixmap( 0 - WHITE_KEY_WIDTH / 2, PIANO_BASE, + *s_blackKeyPressedPm ); + } + else + { + p.drawPixmap( 0 - WHITE_KEY_WIDTH / 2, PIANO_BASE, + *s_blackKeyPm ); + } + } + + // now draw all black keys... + for( int x = 0; x < width(); ) + { + if( KEY_ORDER[cur_key%NOTES_PER_OCTAVE] == BLACK_KEY ) + { + // draw pressed or not pressed key, depending on + // state of current key + if( m_channelTrack->keyPressed( cur_key ) == TRUE ) + { + p.drawPixmap( x + WHITE_KEY_WIDTH / 2, + PIANO_BASE, + *s_blackKeyPressedPm ); + } + else + { + p.drawPixmap( x + WHITE_KEY_WIDTH / 2, + PIANO_BASE, *s_blackKeyPm ); + } + x += WHITE_KEY_WIDTH; + white_cnt = 0; + } + else + { + // simple workaround for increasing x if there were two + // white keys (e.g. between E and F) + ++white_cnt; + if( white_cnt > 1 ) + { + x += WHITE_KEY_WIDTH; + } + } + ++cur_key; + } +#ifndef QT4 + // blit drawn pixmap to actual widget + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + +#include "piano_widget.moc" + diff --git a/src/core/preset_preview_play_handle.cpp b/src/core/preset_preview_play_handle.cpp new file mode 100644 index 000000000..9cc4f2eab --- /dev/null +++ b/src/core/preset_preview_play_handle.cpp @@ -0,0 +1,179 @@ +/* + * preset_preview_play_handle.cpp - implementation of class + * presetPreviewPlayHandle + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include + +#endif + + + +#include "preset_preview_play_handle.h" +#include "note_play_handle.h" +#include "channel_track.h" +#include "track_container.h" +#include "mmp.h" +#include "debug.h" + + +// invisible track-container which is needed as parents for preview-channels +class blindTrackContainer : public trackContainer +{ +public: + static inline blindTrackContainer * inst( void ) + { + if( s_instanceOfMe == NULL ) + { + s_instanceOfMe = new blindTrackContainer(); + } + return( s_instanceOfMe ); + } + + // implement pure-virtual functions... + virtual inline bool fixedTCOs( void ) const + { + return( TRUE ); + } + + virtual inline QString nodeName( void ) const + { + return( "blindtc" ); + } + + +private: + blindTrackContainer( void ) : + trackContainer() + { + hide(); + } + ~blindTrackContainer() + { + } + + static blindTrackContainer * s_instanceOfMe; + + friend void presetPreviewPlayHandle::cleanUp( void ); +} ; + + + +blindTrackContainer * blindTrackContainer::s_instanceOfMe = NULL; + +channelTrack * presetPreviewPlayHandle::s_globalChannelTrack = NULL; +notePlayHandle * presetPreviewPlayHandle::s_globalPreviewNote = NULL; +QMutex * presetPreviewPlayHandle::s_globalDataMutex = NULL; + + +presetPreviewPlayHandle::presetPreviewPlayHandle( + const QString & _preset_file ) : + playHandle(), + m_previewNote( NULL ) +{ + if( s_globalDataMutex == NULL ) + { + s_globalDataMutex = new QMutex; + } + + s_globalDataMutex->lock(); + + if( s_globalPreviewNote != NULL ) + { + s_globalPreviewNote->mute(); + } + + + multimediaProject mmp( _preset_file ); + if( s_globalChannelTrack == NULL ) + { + track * t = track::createTrack( track::CHANNEL_TRACK, + blindTrackContainer::inst() ); + s_globalChannelTrack = dynamic_cast( t ); +#ifdef LMMS_DEBUG + assert( s_globalChannelTrack != NULL ); +#endif + } + s_globalChannelTrack->loadTrackSpecificSettings( mmp.content(). + firstChild(). + toElement() ); + // create temporary note + note n( 0, 0, static_cast( A ), + static_cast( DEFAULT_OCTAVE-1 ), 100 ); + // create note-play-handle for it + m_previewNote = new notePlayHandle( s_globalChannelTrack, 0, ~0, &n ); + //m_previewNote->setFrames( mixer::inst()->sampleRate() ); + + + s_globalPreviewNote = m_previewNote; + + s_globalDataMutex->unlock(); +} + + + + +presetPreviewPlayHandle::~presetPreviewPlayHandle() +{ + s_globalDataMutex->lock(); + if( m_previewNote->muted() == FALSE ) + { + s_globalPreviewNote = NULL; + } + delete m_previewNote; + s_globalDataMutex->unlock(); + //blindTrackContainer::inst()->removeTrack( m_channelTrack ); +} + + + + +void presetPreviewPlayHandle::cleanUp( void ) +{ + delete blindTrackContainer::inst(); +} + + + + +void presetPreviewPlayHandle::play( void ) +{ + m_previewNote->play(); +} + + + + +bool presetPreviewPlayHandle::done( void ) const +{ + return( m_previewNote->muted() ); +} + diff --git a/src/core/sample_play_handle.cpp b/src/core/sample_play_handle.cpp new file mode 100644 index 000000000..9041673f1 --- /dev/null +++ b/src/core/sample_play_handle.cpp @@ -0,0 +1,106 @@ +/* + * sample_play_handle.cpp - implementation of class samplePlayHandle + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" +#ifndef QT4 +#include +#endif + +#include "sample_play_handle.h" +#include "sample_buffer.h" +#include "buffer_allocator.h" + + +samplePlayHandle::samplePlayHandle( const QString & _sample_file ) : + playHandle(), + m_sampleBuffer( new sampleBuffer( _sample_file ) ), + m_ownSampleBuffer( TRUE ), + m_doneMayReturnTrue( TRUE ), + m_frame( 0 ) +{ +} + + +samplePlayHandle::samplePlayHandle( sampleBuffer * _sample_buffer ) : + playHandle(), + m_sampleBuffer( _sample_buffer ), + m_ownSampleBuffer( FALSE ), + m_doneMayReturnTrue( TRUE ), + m_frame( 0 ) +{ +} + + + +samplePlayHandle::~samplePlayHandle() +{ + if( m_ownSampleBuffer == TRUE ) + { + delete m_sampleBuffer; + } +} + + + + +void samplePlayHandle::play( void ) +{ + if( framesDone() >= totalFrames() ) + { + return; + } + + sampleFrame * buf = bufferAllocator::alloc( + mixer::inst()->framesPerAudioBuffer() * + DEFAULT_CHANNELS ); + volumeVector v = { 1.0f, 1.0f +#ifndef DISABLE_SURROUND + , 1.0f, 1.0f +#endif + } ; + m_sampleBuffer->play( buf, m_frame ); + mixer::inst()->addBuffer( buf, mixer::inst()->framesPerAudioBuffer(), + 0, v ); + + bufferAllocator::free( buf ); + + m_frame += mixer::inst()->framesPerAudioBuffer(); +} + + + + +bool samplePlayHandle::done( void ) const +{ + return( framesDone() >= totalFrames() && m_doneMayReturnTrue == TRUE ); +} + + + + +Uint32 samplePlayHandle::totalFrames( void ) const +{ + return( m_sampleBuffer->endFrame() - m_sampleBuffer->startFrame() ); +} + diff --git a/src/core/setup_dialog.cpp b/src/core/setup_dialog.cpp new file mode 100644 index 000000000..68b3a8cd3 --- /dev/null +++ b/src/core/setup_dialog.cpp @@ -0,0 +1,592 @@ +/* + * setup_dialog.cpp - dialog for setting up LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include + +#endif + + +#include "setup_dialog.h" +#include "tab_bar.h" +#include "tab_button.h" +#include "tab_widget.h" +#include "templates.h" +#include "mixer.h" +#include "config_mgr.h" +#include "embed.h" +#include "debug.h" +#include "tooltip.h" +#include "led_checkbox.h" + + +// platform-specific audio-interface-classes +#include "audio_alsa.h" +#include "audio_jack.h" +#include "audio_oss.h" +#include "audio_sdl.h" +#include "audio_dummy.h" + +// platform-specific midi-interface-classes +#include "midi_alsa_raw.h" +#include "midi_oss.h" +#include "midi_dummy.h" + + + +inline void labelWidget( QWidget * _w, const QString & _txt ) +{ + QLabel * title = new QLabel( _txt, _w ); + QFont f = title->font(); + f.setBold( TRUE ); + title->setFont( pointSize<12>( f ) ); + +#ifdef LMMS_DEBUG + assert( dynamic_cast( _w->layout() ) != NULL ); +#endif + dynamic_cast( _w->layout() )->addSpacing( 5 ); + dynamic_cast( _w->layout() )->addWidget( title ); + dynamic_cast( _w->layout() )->addSpacing( 10 ); +} + + + +setupDialog::setupDialog( configTabs _tab_to_open ) : + QDialog(), + m_bufferSize( mixer::inst()->framesPerAudioBuffer() ), + m_disableToolTips( configManager::inst()->value( "tooltips", + "disabled" ).toInt() ), + m_classicalKnobUsability( configManager::inst()->value( "knobs", + "classicalusability" ).toInt() ) +{ + setWindowIcon( embed::getIconPixmap( "setup_general" ) ); + setWindowTitle( tr( "Setup LMMS" ) ); + + QVBoxLayout * vlayout = new QVBoxLayout( this ); + QWidget * settings = new QWidget( this ); + QHBoxLayout * hlayout = new QHBoxLayout( settings ); + + m_tabBar = new tabBar( settings, QBoxLayout::TopToBottom ); + m_tabBar->setExclusive( TRUE ); + m_tabBar->setFixedWidth( 72 ); + + QWidget * ws = new QWidget( settings ); + ws->setFixedSize( 360, 200 ); + + QWidget * general = new QWidget( ws ); + general->setFixedSize( 360, 200 ); + QVBoxLayout * gen_layout = new QVBoxLayout( general ); + labelWidget( general, tr( "General settings" ) ); + + tabWidget * bufsize_tw = new tabWidget( tr( "BUFFER SIZE" ), general ); + bufsize_tw->setFixedHeight( 80 ); + + m_bufSizeSlider = new QSlider( Qt::Horizontal, bufsize_tw ); +#ifdef QT4 + m_bufSizeSlider->setRange( 1, 256 ); + m_bufSizeSlider->setTickPosition( QSlider::TicksBelow ); +#else + m_bufSizeSlider->setMinimum( 1 ); + m_bufSizeSlider->setMaximum( 256 ); + m_bufSizeSlider->setLineStep( 4 ); + m_bufSizeSlider->setTickmarks( QSlider::Below ); +#endif + m_bufSizeSlider->setPageStep( 4 ); + m_bufSizeSlider->setTickInterval( 8 ); + m_bufSizeSlider->setGeometry( 10, 16, 340, 18 ); + m_bufSizeSlider->setValue( m_bufferSize / 64 ); + + connect( m_bufSizeSlider, SIGNAL( valueChanged( int ) ), this, + SLOT( setBufferSize( int ) ) ); + + m_bufSizeLbl = new QLabel( bufsize_tw ); + m_bufSizeLbl->setGeometry( 10, 40, 200, 24 ); + setBufferSize( m_bufSizeSlider->value() ); + + QPushButton * bufsize_reset_btn = new QPushButton( + embed::getIconPixmap( "reload" ), "", bufsize_tw ); + bufsize_reset_btn->setGeometry( 290, 40, 28, 28 ); + connect( bufsize_reset_btn, SIGNAL( clicked() ), this, + SLOT( resetBufSize() ) ); + toolTip::add( bufsize_reset_btn, tr( "Reset to default-value" ) ); + + QPushButton * bufsize_help_btn = new QPushButton( + embed::getIconPixmap( "help" ), "", bufsize_tw ); + bufsize_help_btn->setGeometry( 320, 40, 28, 28 ); + connect( bufsize_help_btn, SIGNAL( clicked() ), this, + SLOT( displayBufSizeHelp() ) ); + + + tabWidget * misc_tw = new tabWidget( tr( "MISC" ), general ); + misc_tw->setFixedHeight( 60 ); + + ledCheckBox * disable_tooltips = new ledCheckBox( + tr( "Disable tooltips (no spurious " + "interrupts while playing)" ), + misc_tw ); + disable_tooltips->move( 10, 18 ); + disable_tooltips->setChecked( m_disableToolTips ); + connect( disable_tooltips, SIGNAL( toggled( bool ) ), + this, SLOT( toggleToolTips( bool ) ) ); + + ledCheckBox * classical_knob_usability = new ledCheckBox( + tr( "Classical knob usability (move " + "cursor around knob to change " + "value)" ), + misc_tw ); + classical_knob_usability->move( 10, 36 ); + classical_knob_usability->setChecked( m_classicalKnobUsability ); + connect( classical_knob_usability, SIGNAL( toggled( bool ) ), + this, SLOT( toggleKnobUsability( bool ) ) ); + + + gen_layout->addWidget( bufsize_tw ); + gen_layout->addSpacing( 10 ); + gen_layout->addWidget( misc_tw ); + gen_layout->addStretch(); + + + + + QWidget * audio = new QWidget( ws ); + audio->setFixedSize( 360, 200 ); + QVBoxLayout * audio_layout = new QVBoxLayout( audio ); + labelWidget( audio, tr( "Audio settings" ) ); + + tabWidget * audioiface_tw = new tabWidget( tr( "AUDIO INTERFACE" ), + audio ); + audioiface_tw->setFixedHeight( 60 ); + + m_audioInterfaces = new QComboBox( audioiface_tw ); + m_audioInterfaces->setGeometry( 10, 20, 240, 22 ); + + + QPushButton * audio_help_btn = new QPushButton( + embed::getIconPixmap( "help" ), "", audioiface_tw ); + audio_help_btn->setGeometry( 320, 20, 28, 28 ); + connect( audio_help_btn, SIGNAL( clicked() ), this, + SLOT( displayAudioHelp() ) ); + + + // create ifaces-settings-widget + QWidget * asw = new QWidget( audio ); + asw->setFixedHeight( 60 ); +#ifndef QT4 + asw->setBackgroundMode( NoBackground ); +#endif + + QHBoxLayout * asw_layout = new QHBoxLayout( asw ); + //asw_layout->setAutoAdd( TRUE ); + +#ifdef JACK_SUPPORT + m_audioIfaceSetupWidgets[audioJACK::name()] = + new audioJACK::setupWidget( asw ); +#endif + +#ifdef ALSA_SUPPORT + m_audioIfaceSetupWidgets[audioALSA::name()] = + new audioALSA::setupWidget( asw ); +#endif + +#ifdef SDL_AUDIO_SUPPORT + m_audioIfaceSetupWidgets[audioSDL::name()] = + new audioSDL::setupWidget( asw ); +#endif + +#ifdef OSS_SUPPORT + m_audioIfaceSetupWidgets[audioOSS::name()] = + new audioOSS::setupWidget( asw ); +#endif + m_audioIfaceSetupWidgets[audioDummy::name()] = + new audioDummy::setupWidget( asw ); + + + for( aswMap::iterator it = m_audioIfaceSetupWidgets.begin(); + it != m_audioIfaceSetupWidgets.end(); ++it ) + { +#ifdef QT4 + it.value()->hide(); + asw_layout->addWidget( it.value() ); +#else + it.data()->hide(); + asw_layout->addWidget( it.data() ); +#endif + m_audioInterfaces->addItem( it.key() ); + } +#ifdef QT4 + m_audioInterfaces->setCurrentIndex( m_audioInterfaces->findText( + mixer::inst()->audioDevName() ) ); +#else + m_audioInterfaces->setCurrentText( mixer::inst()->audioDevName() ); +#endif + m_audioIfaceSetupWidgets[mixer::inst()->audioDevName()]->show(); + + connect( m_audioInterfaces, SIGNAL( activated( const QString & ) ), + this, SLOT( audioInterfaceChanged( const QString & ) ) ); + + + audio_layout->addWidget( audioiface_tw ); + audio_layout->addSpacing( 20 ); + audio_layout->addWidget( asw ); + audio_layout->addStretch(); + + + + + QWidget * midi = new QWidget( ws ); + QVBoxLayout * midi_layout = new QVBoxLayout( midi ); + labelWidget( midi, tr( "MIDI settings" ) ); + + tabWidget * midiiface_tw = new tabWidget( tr( "MIDI INTERFACE" ), + midi ); + midiiface_tw->setFixedHeight( 60 ); + + m_midiInterfaces = new QComboBox( midiiface_tw ); + m_midiInterfaces->setGeometry( 10, 20, 240, 22 ); + + + QPushButton * midi_help_btn = new QPushButton( + embed::getIconPixmap( "help" ), "", midiiface_tw ); + midi_help_btn->setGeometry( 320, 20, 28, 28 ); + connect( midi_help_btn, SIGNAL( clicked() ), this, + SLOT( displayMIDIHelp() ) ); + + + // create ifaces-settings-widget + QWidget * msw = new QWidget( midi ); + msw->setFixedHeight( 60 ); +#ifndef QT4 + msw->setBackgroundMode( NoBackground ); +#endif + + QHBoxLayout * msw_layout = new QHBoxLayout( msw ); + //msw_layout->setAutoAdd( TRUE ); + +#ifdef ALSA_SUPPORT + m_midiIfaceSetupWidgets[midiALSARaw::name()] = + new midiALSARaw::setupWidget( msw ); +#endif + +#ifdef OSS_SUPPORT + m_midiIfaceSetupWidgets[midiOSS::name()] = + new midiOSS::setupWidget( msw ); +#endif + m_midiIfaceSetupWidgets[midiDummy::name()] = + new midiDummy::setupWidget( msw ); + + + for( mswMap::iterator it = m_midiIfaceSetupWidgets.begin(); + it != m_midiIfaceSetupWidgets.end(); ++it ) + { +#ifdef QT4 + it.value()->hide(); + msw_layout->addWidget( it.value() ); +#else + msw_layout->addWidget( it.data() ); + it.data()->hide(); +#endif + m_midiInterfaces->addItem( it.key() ); + } + +#ifdef QT4 + m_midiInterfaces->setCurrentIndex( m_midiInterfaces->findText( + mixer::inst()->midiDevName() ) ); +#else + m_midiInterfaces->setCurrentText( mixer::inst()->midiDevName() ); +#endif + m_midiIfaceSetupWidgets[mixer::inst()->midiDevName()]->show(); + + connect( m_midiInterfaces, SIGNAL( activated( const QString & ) ), + this, SLOT( midiInterfaceChanged( const QString & ) ) ); + + + midi_layout->addWidget( midiiface_tw ); + midi_layout->addSpacing( 20 ); + midi_layout->addWidget( msw ); + midi_layout->addStretch(); + + +#ifndef QT +#define setIcon setPixmap +#endif + + m_tabBar->addTab( general, tr( "General settings" ), 0, FALSE, TRUE + )->setIcon( embed::getIconPixmap( "setup_general" ) ); + m_tabBar->addTab( audio, tr( "Audio settings" ), 1, FALSE, TRUE + )->setIcon( embed::getIconPixmap( "setup_audio" ) ); + m_tabBar->addTab( midi, tr( "MIDI settings" ), 2, TRUE, TRUE + )->setIcon( embed::getIconPixmap( "setup_midi" ) ); + +#undef setIcon + + m_tabBar->setActiveTab( _tab_to_open ); + + hlayout->addWidget( m_tabBar ); + hlayout->addSpacing( 10 ); + hlayout->addWidget( ws ); + hlayout->addSpacing( 10 ); + hlayout->addStretch(); + + QWidget * buttons = new QWidget( this ); + QHBoxLayout * btn_layout = new QHBoxLayout( buttons ); + QPushButton * ok_btn = new QPushButton( embed::getIconPixmap( "apply" ), + tr( "OK" ), buttons ); + connect( ok_btn, SIGNAL( clicked() ), this, SLOT( accept() ) ); + + QPushButton * cancel_btn = new QPushButton( embed::getIconPixmap( + "cancel" ), + tr( "Cancel" ), + buttons ); + connect( cancel_btn, SIGNAL( clicked() ), this, SLOT( reject() ) ); + + btn_layout->addStretch(); + btn_layout->addSpacing( 10 ); + btn_layout->addWidget( ok_btn ); + btn_layout->addSpacing( 10 ); + btn_layout->addWidget( cancel_btn ); + btn_layout->addSpacing( 10 ); + + vlayout->addWidget( settings ); + vlayout->addSpacing( 10 ); + vlayout->addWidget( buttons ); + vlayout->addSpacing( 10 ); + vlayout->addStretch(); + + show(); + + +} + + + + +setupDialog::~setupDialog() +{ +} + + + + +void setupDialog::accept( void ) +{ + configManager::inst()->setValue( "mixer", "framesperaudiobuffer", + QString::number( m_bufferSize ) ); + configManager::inst()->setValue( "mixer", "audiodev", + m_audioInterfaces->currentText() ); + configManager::inst()->setValue( "mixer", "mididev", + m_midiInterfaces->currentText() ); + configManager::inst()->setValue( "tooltips", "disabled", + QString::number( m_disableToolTips ) ); + configManager::inst()->setValue( "knobs", "classicalusability", + QString::number( m_classicalKnobUsability ) ); + // tell all audio-settings-widget to save their settings + for( aswMap::iterator it = m_audioIfaceSetupWidgets.begin(); + it != m_audioIfaceSetupWidgets.end(); ++it ) + { +#ifdef QT4 + it.value()->saveSettings(); +#else + it.data()->saveSettings(); +#endif + } + // tell all MIDI-settings-widget to save their settings + for( mswMap::iterator it = m_midiIfaceSetupWidgets.begin(); + it != m_midiIfaceSetupWidgets.end(); ++it ) + { +#ifdef QT4 + it.value()->saveSettings(); +#else + it.data()->saveSettings(); +#endif + } + + configManager::inst()->saveConfigFile(); + + QDialog::accept(); + QMessageBox::information( NULL, tr( "Restart LMMS" ), + tr( "Please note that most changes " + "won't take effect until " + "you restart LMMS!" ), + QMessageBox::Ok ); +} + + + + +void setupDialog::setBufferSize( int _value ) +{ + if( m_bufSizeSlider->value() != _value ) + { + m_bufSizeSlider->setValue( _value ); + } + + m_bufferSize = _value * 64; + m_bufSizeLbl->setText( tr( "FRAMES: %1\nLATENCY: %2 ms" ).arg( + m_bufferSize ).arg( + 1000.0f * m_bufferSize / + mixer::inst()->sampleRate(), + 0, 'f', 1 ) ); +} + + + + +void setupDialog::resetBufSize( void ) +{ + setBufferSize( DEFAULT_BUFFER_SIZE / 64 ); +} + + + + +void setupDialog::displayBufSizeHelp( void ) +{ +#ifdef QT4 + QWhatsThis::showText( QCursor::pos(), +#else + QWhatsThis::display( +#endif + tr( "Here you can setup the internal buffer-size " + "used by LMMS. Smaller values result " + "in a lower latency but also may cause " + "unusable sound or bad performance, " + "especially on older computers or " + "systems with a non-realtime " + "kernel." ) ); +} + + + + +void setupDialog::audioInterfaceChanged( const QString & _iface ) +{ + for( aswMap::iterator it = m_audioIfaceSetupWidgets.begin(); + it != m_audioIfaceSetupWidgets.end(); ++it ) + { +#ifdef QT4 + it.value()->hide(); +#else + it.data()->hide(); +#endif + } + + m_audioIfaceSetupWidgets[_iface]->show(); +} + + + + +void setupDialog::displayAudioHelp( void ) +{ +#ifdef QT4 + QWhatsThis::showText( QCursor::pos(), +#else + QWhatsThis::display( +#endif + tr( "Here you can select your preferred " + "audio-interface. Depending on the " + "configuration of your system during " + "compilation time you can choose " + "between ALSA, JACK, OSS and more. " + "Below you see a box which offers " + "controls to setup the selected " + "audio-interface." ) ); +} + + + + +void setupDialog::midiInterfaceChanged( const QString & _iface ) +{ + for( mswMap::iterator it = m_midiIfaceSetupWidgets.begin(); + it != m_midiIfaceSetupWidgets.end(); ++it ) + { +#ifdef QT4 + it.value()->hide(); +#else + it.data()->hide(); +#endif + } + + m_midiIfaceSetupWidgets[_iface]->show(); +} + + + + +void setupDialog::displayMIDIHelp( void ) +{ +#ifdef QT4 + QWhatsThis::showText( QCursor::pos(), +#else + QWhatsThis::display( +#endif + tr( "Here you can select your preferred " + "MIDI-interface. Depending on the " + "configuration of your system during " + "compilation time you can choose " + "between ALSA, OSS and more. " + "Below you see a box which offers " + "controls to setup the selected " + "MIDI-interface." ) ); +} + + + + +void setupDialog::toggleToolTips( bool _disabled ) +{ + m_disableToolTips = _disabled; +} + + + + +void setupDialog::toggleKnobUsability( bool _classical ) +{ + m_classicalKnobUsability = _classical; +} + + + + +#include "setup_dialog.moc" + diff --git a/src/core/song_editor.cpp b/src/core/song_editor.cpp new file mode 100644 index 000000000..edc6d8193 --- /dev/null +++ b/src/core/song_editor.cpp @@ -0,0 +1,1695 @@ +/* + * song_editor.cpp - basic window for editing song + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + + +#include "song_editor.h" +#include "bb_editor.h" +#include "rename_dialog.h" +#include "embed.h" +#include "templates.h" +#include "export_project_dialog.h" +#include "bb_track.h" +#include "channel_track.h" +#include "mmp.h" +#include "midi_device.h" +#include "timeline.h" +#include "pattern.h" +#include "piano_roll.h" +#include "envelope_and_lfo_widget.h" +#include "visualization_widget.h" +#include "project_notes.h" +#include "config_mgr.h" +#include "midi_file.h" +#include "lcd_spinbox.h" +#include "tooltip.h" + +#include "debug.h" + + +#include "triple_oscillator.h" + + +extern QString file_to_load; +extern QString file_to_render; + + +const int SCROLLBAR_SIZE = 16; + + +songEditor * songEditor::s_instanceOfMe = NULL; + + +songEditor::songEditor() : + trackContainer(), + m_fileName( "" ), + m_oldFileName( "" ), + m_exporting( FALSE ), + m_playing( FALSE ), + m_paused( FALSE ), + m_playMode( PLAY_SONG ), + m_trackToPlay( NULL ), + m_patternToPlay( NULL ), + m_loopPattern( FALSE ), + m_scrollBack( FALSE ), + m_epd( NULL ), + m_shiftPressed( FALSE ), + m_controlPressed( FALSE ) +{ + // hack, because code called out of this function uses + // songEditor::inst(), which assigns s_instanceOfMe after return + // of this function... + s_instanceOfMe = this; + + setWindowTitle( tr( "Song-Editor" ) ); + setWindowIcon( embed::getIconPixmap( "songeditor" ) ); + setGeometry( 10, 10, 640, 300 ); + show(); + +#ifdef QT4 + setFocusPolicy( Qt::StrongFocus ); +#else + setFocusPolicy( StrongFocus ); +#endif + setFocus(); + + + QWidget * cw = new QWidget( this ); + setCentralWidget( cw ); + + // create time-line + timeLine * tl = new timeLine( TRACK_OP_WIDTH + + DEFAULT_SETTINGS_WIDGET_WIDTH, 0, + pixelsPerTact(), m_playPos[PLAY_SONG], + m_currentPosition, cw ); + connect( this, SIGNAL( positionChanged( const midiTime & ) ), + m_playPos[PLAY_SONG].m_timeLine, + SLOT( updatePosition( const midiTime & ) ) ); + connect( tl, SIGNAL( positionChanged( const midiTime & ) ), + this, SLOT( updatePosition( const midiTime & ) ) ); + +#ifdef QT4 + containerWidget()->setParent( cw ); +#else + containerWidget()->reparent( cw, 0, QPoint( 0, 0 ) ); +#endif + containerWidget()->move( 0, tl->height() ); + + + QToolBar * song_control = new QToolBar( tr( "Song control" ), this ); +#ifdef QT4 + addToolBar( Qt::TopToolBarArea, song_control ); +#else + addDockWindow( song_control, tr( "Song control" ), Qt::DockTop, + FALSE ); +#endif +/* song_control->setPaletteBackgroundPixmap( embed::getIconPixmap( + "toolbar_bg" ) ); + song_control->setErasePixmap( embed::getIconPixmap( "toolbar_bg" ) );*/ + +#ifdef QT4 + QAction * a; + + a = song_control->addAction( embed::getIconPixmap( "play" ), + tr( "Play song (Space)" ), + this, SLOT( play() ) ); + a->setToolTip( tr( "Play/pause song (Space)" ) ); + a->setWhatsThis( tr( "Click here, if you want to play your whole song. " + "Playing will be started at the song-position-" + "marker (green). You can also move it while " + "playing." ) ); +#else + m_playButton = new QToolButton( embed::getIconPixmap( "play" ), + tr( "Play song (Space)" ), + QString::null, this, SLOT( play() ), + song_control ); +#endif +#ifdef QT4 + a = song_control->addAction( embed::getIconPixmap( "stop" ), + tr( "Stop song (Space)" ), + this, SLOT( stop() ) ); + a->setToolTip( tr( "Stop song (Space)" ) ); + a->setWhatsThis( tr( "Click here, if you want to stop playing of your " + "song. The song-position-marker will be set to " + "the start of your song." ) ); +#else + m_stopButton = new QToolButton( embed::getIconPixmap( "stop" ), + tr( "Stop song (Space)" ), + QString::null, this, SLOT( stop() ), + song_control ); +#endif + + + song_control->addSeparator(); + + // spacer-item + ( new QWidget( song_control ) )->setFixedSize( 10, 1 ); + + + QLabel * bpm_label = new QLabel( song_control ); + bpm_label->setPixmap( embed::getIconPixmap( "clock" ) ); + + // spacer-item + ( new QWidget( song_control ) )->setFixedSize( 8, 1 ); + + + m_bpmSpinBox = new lcdSpinBox( MIN_BPM, MAX_BPM, 3, song_control ); +#ifdef QT4 + song_control->addWidget( m_bpmSpinBox ); + song_control->addWidget( bpm_label ); +#endif + m_bpmSpinBox->setLabel( tr( "TEMPO/BPM" ) ); + connect( m_bpmSpinBox, SIGNAL( valueChanged( int ) ), this, + SLOT( setBPM( int ) ) ); + toolTip::add( m_bpmSpinBox, tr( "tempo of song" ) ); + +#ifdef QT4 + m_bpmSpinBox->setWhatsThis( +#else + QWhatsThis::add( m_bpmSpinBox, +#endif + tr( "The tempo of a song is specified in beats per minute " + "(BPM). If you want to change the tempo of your " + "song, change this value. Every tact has four beats, " + "so the tempo in BPM specifies, how many tacts / 4 " + "should be played within a minute (or how many tacts " + "should be played within four minutes)." ) ); + + // spacer-item + ( new QWidget( song_control ) )->setFixedSize( 10, 1 ); + + + song_control->addSeparator(); + + + QLabel * master_vol_lbl = new QLabel( song_control ); + master_vol_lbl->setPixmap( embed::getIconPixmap( "master_volume" ) ); + +#ifdef QT4 + m_masterVolumeSlider = new QSlider( Qt::Vertical, song_control ); + m_masterVolumeSlider->setRange( 0, 200 ); + m_masterVolumeSlider->setPageStep( 10 ); + m_masterVolumeSlider->setValue( 100 ); + m_masterVolumeSlider->setTickPosition( QSlider::TicksLeft ); + song_control->addWidget( master_vol_lbl ); + song_control->addWidget( m_masterVolumeSlider ); +#else + m_masterVolumeSlider = new QSlider( 0, 200, 10, 100, Qt::Vertical, + song_control ); + m_masterVolumeSlider->setTickPosition( QSlider::Left ); +#endif + m_masterVolumeSlider->setFixedSize( 26, 48 ); + m_masterVolumeSlider->setTickInterval( 50 ); + toolTip::add( m_masterVolumeSlider, tr( "master output volume" ) ); + + connect( m_masterVolumeSlider, SIGNAL( valueChanged( int ) ), this, + SLOT( masterVolumeChanged( int ) ) ); + connect( m_masterVolumeSlider, SIGNAL( sliderPressed() ), this, + SLOT( masterVolumePressed() ) ); + connect( m_masterVolumeSlider, SIGNAL( sliderMoved( int ) ), this, + SLOT( masterVolumeMoved( int ) ) ); + connect( m_masterVolumeSlider, SIGNAL( sliderReleased() ), this, + SLOT( masterVolumeReleased() ) ); + + + // spacer-item + ( new QWidget( song_control ) )->setFixedSize( 10, 1 ); + + QLabel * master_pitch_lbl = new QLabel( song_control ); + master_pitch_lbl->setPixmap( embed::getIconPixmap( "master_pitch" ) ); + +#ifdef QT4 + m_masterPitchSlider = new QSlider( Qt::Vertical, song_control ); + m_masterPitchSlider->setRange( -12, 12 ); + m_masterPitchSlider->setPageStep( 1 ); + m_masterPitchSlider->setValue( 0 ); + m_masterPitchSlider->setTickPosition( QSlider::TicksLeft ); + song_control->addWidget( master_pitch_lbl ); + song_control->addWidget( m_masterPitchSlider ); +#else + m_masterPitchSlider = new QSlider( -12, 12, 1, 0, Qt::Vertical, + song_control ); + m_masterPitchSlider->setTickPosition( QSlider::Left ); +#endif + m_masterPitchSlider->setFixedSize( 26, 48 ); + m_masterPitchSlider->setTickInterval( 12 ); + toolTip::add( m_masterPitchSlider, tr( "master pitch" ) ); + connect( m_masterPitchSlider, SIGNAL( valueChanged( int ) ), this, + SLOT( masterPitchChanged( int ) ) ); + connect( m_masterPitchSlider, SIGNAL( sliderPressed() ), this, + SLOT( masterPitchPressed() ) ); + connect( m_masterPitchSlider, SIGNAL (sliderMoved( int) ), this, + SLOT( masterPitchMoved( int ) ) ); + connect( m_masterPitchSlider, SIGNAL( sliderReleased() ), this, + SLOT( masterPitchReleased() ) ); + + // spacer-item + ( new QWidget( song_control ) )->setFixedSize( 5, 1 ); + + song_control->addSeparator(); + + // spacer-item + ( new QWidget( song_control ) )->setFixedSize( 5, 1 ); + + m_masterOutputGraph = new visualizationWidget( embed::getIconPixmap( + "output_graph" ), song_control ); +#ifdef QT4 + song_control->addWidget( m_masterOutputGraph ); +#endif + // live high-quality mode switching is somewhat experimental so we don't + // offer it... +/* QToolButton * hq = new QToolButton( + embed::getIconPixmap( "presetfile" ), + tr( "High quality mode" ), + QString::null, NULL, NULL, + song_control ); + hq->setToggleButton( TRUE ); + connect( hq, SIGNAL( toggled( bool ) ), mixer::inst(), + SLOT( setHighQuality( bool ) ) );*/ + + + m_leftRightScroll = new QScrollBar( Qt::Horizontal, cw ); + m_leftRightScroll->setMinimum( 0 ); + m_leftRightScroll->setMaximum( 0 ); +#ifdef QT4 + m_leftRightScroll->setSingleStep( 1 ); + m_leftRightScroll->setPageStep( 20 ); +#else + m_leftRightScroll->setSteps( 1, 20 ); +#endif + connect( m_leftRightScroll, SIGNAL( valueChanged( int ) ), this, + SLOT( scrolled( int ) ) ); + + + QToolBar * edit_tb = new QToolBar( tr( "Edit" ), this ); +#ifdef QT4 + addToolBar( Qt::TopToolBarArea, edit_tb ); +#else + addDockWindow( edit_tb, tr( "Edit" ), Qt::DockTop, FALSE ); +#endif +/* edit_tb->setPaletteBackgroundPixmap( embed::getIconPixmap( + "toolbar_bg" ) ); + edit_tb->setErasePixmap( embed::getIconPixmap( "toolbar_bg" ) );*/ +#ifdef QT4 + a = edit_tb->addAction( embed::getIconPixmap( "add_channel_track" ), "", + this, SLOT( addChannelTrack() ) ); + a->setToolTip( tr( "Add channel-track" ) ); +#else + m_addChannelTrackButton = new QToolButton( embed::getIconPixmap( + "add_channel_track" ), "", "", + this, SLOT( addChannelTrack() ), + edit_tb ); +#endif +#ifdef QT4 + a = edit_tb->addAction( embed::getIconPixmap( "add_bb_track" ), "", + this, SLOT( addBBTrack() ) ); + a->setToolTip( tr( "Add beat/bassline" ) ); +#else + m_addBBTrackButton = new QToolButton( embed::getIconPixmap( + "add_bb_track" ), "", "", + this, SLOT( addBBTrack() ), + edit_tb ); +#endif +#ifdef QT4 + a = edit_tb->addAction( embed::getIconPixmap( "add_sample_track" ), "", + this, SLOT( addSampleTrack() ) ); + a->setToolTip( tr( "Add sample-track" ) ); +#else + m_addSampleTrackButton = new QToolButton( embed::getIconPixmap( + "add_sample_track" ), "", "", + this, SLOT( addSampleTrack() ), + edit_tb ); + +#endif + + edit_tb->addSeparator(); + +#ifdef QT4 + a = edit_tb->addAction( embed::getIconPixmap( "se_insert_tact" ), "", + this, SLOT( insertTact() ) ); + a->setToolTip( tr( "Insert bar at current tact (Shift+Insert)" ) ); + a->setWhatsThis( tr( "If you click here, a tact will be inserted at " + "the current tact." ) ); +#else + m_insertTactButton = new QToolButton( embed::getIconPixmap( + "se_insert_tact" ), "", "", + this, SLOT( insertTact() ), + edit_tb ); +#endif +#ifdef QT4 + a = edit_tb->addAction( embed::getIconPixmap( "se_remove_tact" ), "", + this, SLOT( removeTact() ) ); + a->setToolTip( tr( "Remove bar at current tact (Shift+Delete)" ) ); + a->setWhatsThis( tr( "If you click here, the tact at the current tact " + "will be removed." ) ); +#else + m_removeTactButton = new QToolButton( embed::getIconPixmap( + "se_remove_tact" ), "", "", + this, SLOT( removeTact() ), + edit_tb ); +#endif + + // add tooltips and whats-this-texts to all buttons + + toolTip::add( m_playButton, tr( "Play/pause song (Space)" ) ); + toolTip::add( m_stopButton, tr( "Stop playing song (Space)" ) ); + toolTip::add( m_addChannelTrackButton, tr( "Add channel-track" ) ); + toolTip::add( m_addBBTrackButton, tr( "Add beat/bassline" ) ); + toolTip::add( m_addSampleTrackButton, tr( "Add sample-track" ) ); + toolTip::add( m_insertTactButton, tr( "Insert tact at current tact " + "(Shift+Insert)" ) ); + toolTip::add( m_removeTactButton, tr( "Remove tact at current tact " + "(Shift+Delete)" ) ); +#ifdef QT4 +#else + QWhatsThis::add( m_playButton, tr( "Click here, if you want to play " + "your whole song. Playing will " + "be started at the " + "song-position-marker (green). " + "You can also move it while " + "playing." ) ); + QWhatsThis::add( m_stopButton, tr ( "Click here, if you want to stop " + "playing of your song. The " + "song-position-marker will be " + "set to the start of your song." + ) ); + QWhatsThis::add( m_insertTactButton, tr( "If you click here, a " + "tact will " + "be inserted at the " + "current tact." ) ); + QWhatsThis::add( m_removeTactButton, tr( "If you click here, the " + "tact at the " + "current tact will be " + "removed." ) ); +#endif + + m_projectNotes = new projectNotes(); + m_projectNotes->resize( 300, 200 ); + m_projectNotes->move( 640, 10 ); + m_projectNotes->show(); + + + // we try to load given file + if( file_to_load != "" ) + { + loadProject( file_to_load ); + } + else + { + createNewProject(); + } +} + + + + +songEditor::~songEditor() +{ +} + + + + +void songEditor::closeEvent( QCloseEvent * _ce ) +{ + _ce->ignore(); + hide(); +} + + + + +void songEditor::paintEvent( QPaintEvent * _pe ) +{ + m_leftRightScroll->setMaximum( lengthInTacts() ); + trackContainer::paintEvent( _pe ); +} + + + + +// responsible for moving scrollbars after resizing +void songEditor::resizeEvent( QResizeEvent * _re ) +{ + m_leftRightScroll->setGeometry( 0, + centralWidget()->height() - 2 - + SCROLLBAR_SIZE, + centralWidget()->width() - + SCROLLBAR_SIZE, + SCROLLBAR_SIZE ); + + m_playPos[PLAY_SONG].m_timeLine->setFixedWidth( + centralWidget()->width() ); + trackContainer::resizeEvent( _re ); +} + + + + +void songEditor::keyPressEvent( QKeyEvent * _ke ) +{ + if( _ke->key() == Qt::Key_Shift ) + { + m_shiftPressed = TRUE; + } + else + { + m_shiftPressed = FALSE; + } + if( _ke->key() == Qt::Key_Control ) + { + m_controlPressed = TRUE; + } + else + { + m_controlPressed = FALSE; + } + + if( _ke->modifiers() & Qt::ShiftModifier && + _ke->key() == Qt::Key_Insert ) + { + insertTact(); + } + else if( _ke->modifiers() & Qt::ShiftModifier && + _ke->key() == Qt::Key_Delete ) + { + removeTact(); + } + else if( _ke->key() == Qt::Key_Left ) + { + tact interesting_tact = currentTact(); + if( interesting_tact > 0 ) + { + setPlayPos( --interesting_tact, currentTact64th(), + PLAY_SONG ); + } + + } + else if( _ke->key() == Qt::Key_Right ) + { + tact interesting_tact = currentTact(); + if( interesting_tact < MAX_SONG_LENGTH ) + { + setPlayPos( ++interesting_tact, currentTact64th(), + PLAY_SONG ); + } + + } + else if( _ke->key() == Qt::Key_Space ) + { + if( playing() ) + { + stop(); + } + else + { + play(); + } + } + else if( _ke->key() == Qt::Key_Home ) + { + setPlayPos( 0, 0, PLAY_SONG ); + } + else + { + _ke->ignore(); + } +} + + + + + +void songEditor::scrolled( int _new_pos ) +{ + update(); + emit positionChanged( m_currentPosition = midiTime( _new_pos, 0 ) ); +} + + + + +void songEditor::wheelEvent( QWheelEvent * _we ) +{ + if( m_controlPressed ) + { + if( _we->delta() > 0 ) + { + setPixelsPerTact( (int) tMin( pixelsPerTact() * 2, + 256.0f ) ); + } + else if( pixelsPerTact() >= 8 ) + { + setPixelsPerTact( (int) pixelsPerTact() / 2 ); + } + m_playPos[PLAY_SONG].m_timeLine->setPixelsPerTact( + pixelsPerTact() ); + realignTracks( TRUE ); + } + else if( m_shiftPressed ) + { + m_leftRightScroll->setValue( m_leftRightScroll->value() - + _we->delta() / 30 ); + } +} + + + + +void songEditor::masterVolumeChanged( int _new_val ) +{ + mixer::inst()->setMasterOutput( 2.0f - _new_val / 100.0f ); + setModified(); +} + + + + +void songEditor::masterVolumePressed( void ) +{ + masterVolumeMoved( m_masterVolumeSlider->value() ); +} + + + + +void songEditor::masterVolumeMoved( int _new_val ) +{ + lmmsMainWin::inst()->statusBar()->showMessage( tr( + "Master output volume:" ) + + " " + QString::number( 200 - _new_val ) + "%" ); +} + + + + +void songEditor::masterVolumeReleased( void ) +{ + lmmsMainWin::inst()->statusBar()->clearMessage(); +} + + + + +void songEditor::masterPitchChanged( int _new_val ) +{ + setModified(); +} + + + + +void songEditor::masterPitchPressed( void ) +{ + masterPitchMoved( m_masterPitchSlider->value() ); +} + + + + +void songEditor::masterPitchMoved( int _new_val ) +{ + lmmsMainWin::inst()->statusBar()->showMessage( tr( + "Master output pitch:" ) + + " " + QString::number( -_new_val ) + " " + tr( "semitones" ) ); + +} + + + + +void songEditor::masterPitchReleased( void ) +{ + lmmsMainWin::inst()->statusBar()->clearMessage(); +} + + + + +void songEditor::toggleHQMode( void ) +{ + //mixer::inst()->setHighQuality (hq_btn->isChecked()); +} + + + + +void songEditor::updatePosition( const midiTime & _t ) +{ + if( ( m_playing && m_playMode == PLAY_SONG ) || m_scrollBack == TRUE ) + { + const int w = centralWidget()->width() - + DEFAULT_SETTINGS_WIDGET_WIDTH - TRACK_OP_WIDTH; + if( _t > m_currentPosition + w * 64 / pixelsPerTact() ) + { + m_leftRightScroll->setValue( _t.getTact() ); + } + else if( _t < m_currentPosition ) + { + midiTime t = tMax( (int)( _t - w * 64 * 64 / + pixelsPerTact() ), + 0 ); + m_leftRightScroll->setValue( t.getTact() ); + } + m_scrollBack = FALSE; + } +} + + + + +void songEditor::setBPM( int _new_bpm ) +{ + m_bpmSpinBox->setValue( tLimit( _new_bpm, MIN_BPM, MAX_BPM ) ); + setModified(); +} + + + + +int songEditor::masterPitch( void ) const +{ + return( -m_masterPitchSlider->value() ); +} + + + + +void songEditor::doActions( void ) +{ + while( !m_actions.isEmpty() ) + { + timeLine * tl = m_playPos[m_playMode].m_timeLine; + switch( m_actions.front() ) + { + case ACT_STOP_PLAY: + { + m_playing = FALSE; + if( tl != NULL ) + { + + switch( tl->behaviourAtStop() ) + { + case timeLine::BACK_TO_ZERO: + m_playPos[m_playMode].setTact( 0 ); + m_playPos[m_playMode].setTact64th( 0 ); + break; + + case timeLine::BACK_TO_START: + if( tl->savedPos() >= 0 ) + { + m_playPos[m_playMode].setTact( + tl->savedPos().getTact() ); + m_playPos[m_playMode].setTact64th( + tl->savedPos().getTact64th() ); + tl->savePos( -1 ); + } + break; + + case timeLine::KEEP_STOP_POSITION: + default: + break; + } + + } + else + { + m_playPos[m_playMode].setTact( 0 ); + m_playPos[m_playMode].setTact64th( 0 ); + } + + m_playPos[m_playMode].setCurrentFrame( 0 ); + updateTimeLinePosition(); + + // remove all note-play-handles that are active + mixer::inst()->clear(); + + break; + } + + case ACT_PLAY_SONG: + m_playMode = PLAY_SONG; + m_playing = TRUE; + break; + + case ACT_PLAY_TRACK: + m_playMode = PLAY_TRACK; + m_playing = TRUE; + break; + + case ACT_PLAY_BB: + m_playMode = PLAY_BB; + m_playing = TRUE; + break; + + case ACT_PLAY_PATTERN: + m_playMode = PLAY_PATTERN; + m_playing = TRUE; + break; + + case ACT_PAUSE: + m_playing = FALSE;// just set the play-flag + m_paused = TRUE; + break; + + case ACT_RESUME_FROM_PAUSE: + m_playing = TRUE;// just set the play-flag + m_paused = FALSE; + break; + } + + // a second switch for saving pos when starting to play + // anything + switch( m_actions.front() ) + { + case ACT_PLAY_SONG: + case ACT_PLAY_TRACK: + case ACT_PLAY_BB: + case ACT_PLAY_PATTERN: + if( tl != NULL ) + { + tl->savePos( m_playPos[m_playMode] ); + } + break; + + // keep GCC happy... + default: + break; + } + + m_actions.erase( m_actions.begin() ); + + } + +} + + + + +void songEditor::processNextBuffer( void ) +{ + doActions(); + + if( m_playing == FALSE ) + { + return; + } + + trackVector tv; + Sint16 tco_num = -1; + + switch( m_playMode ) + { + case PLAY_SONG: + tv = tracks(); + // at song-start we have to reset the LFOs + if( m_playPos[PLAY_SONG] == 0 ) + { + envelopeAndLFOWidget::resetLFO(); + } + break; + + case PLAY_TRACK: + tv.push_back( m_trackToPlay ); + break; + + case PLAY_BB: + if( bbEditor::inst()->numOfBBs() > 0 ) + { + tco_num = bbEditor::inst()->currentBB(); + tv.push_back( bbTrack::findBBTrack( tco_num ) ); + } + break; + + case PLAY_PATTERN: + tco_num = m_patternToPlay->getTrack()->getTCONum( + m_patternToPlay ); + tv.push_back( m_patternToPlay->getTrack() ); + break; + + default: + return; + + } + + if( tv.isEmpty() == TRUE ) + { + return; + } + + // check for looping-mode and act if neccessary + timeLine * tl = m_playPos[m_playMode].m_timeLine; + if( tl != NULL && m_exporting == FALSE && tl->loopPointsEnabled() ) + { + if( m_playPos[m_playMode] < tl->loopBegin() || + m_playPos[m_playMode] >= tl->loopEnd() ) + { + m_playPos[m_playMode].setTact( + tl->loopBegin().getTact() ); + m_playPos[m_playMode].setTact64th( + tl->loopBegin().getTact64th() ); + // force reset of current-frame-var afterwards + m_playPos[m_playMode].setCurrentFrame( 0 ); + } + } + + Uint32 total_frames_played = 0; + Uint32 frames_per_tact = static_cast( framesPerTact() ); + if( m_playPos[m_playMode].currentFrame() == 0 && + m_playPos[m_playMode].getTact64th() > 0 ) + { + m_playPos[m_playMode].setCurrentFrame( + m_playPos[m_playMode].getTact64th() * + frames_per_tact / 64 ); + } + + while( total_frames_played < mixer::inst()->framesPerAudioBuffer() ) + { + Uint32 played_frames = mixer::inst()->framesPerAudioBuffer() - + total_frames_played; + + // did we play a whole tact? + if( m_playPos[m_playMode].currentFrame() >= frames_per_tact ) + { + // per default we just continue playing even if + // there's no more stuff to play (song-play-mode) + int max_tact = m_playPos[m_playMode].getTact() + 2; + + // then decide whether to go over to next tact or to + // loop back to first tact + if( m_playMode == PLAY_BB ) + { + max_tact = + bbEditor::inst()->lengthOfCurrentBB(); + } + else if( m_playMode == PLAY_PATTERN && + m_loopPattern == TRUE && + tl != NULL && + tl->loopPointsEnabled() == FALSE ) + { + max_tact = m_patternToPlay->length().getTact(); + } + if( m_playPos[m_playMode].getTact() + 1 < max_tact ) + { + // next tact + m_playPos[m_playMode].setTact( + m_playPos[m_playMode].getTact() + 1 ); + } + else + { + // first tact + m_playPos[m_playMode].setTact( 0 ); + } + m_playPos[m_playMode].setCurrentFrame( 0 ); + } + // or do we have some samples left in this tact but this are + // less then samples we have to play? + else if( frames_per_tact - m_playPos[m_playMode].currentFrame() + < mixer::inst()->framesPerAudioBuffer() ) + { + // then set played_samples to remaining samples, the + // rest will be played in next loop + played_frames = frames_per_tact - + m_playPos[m_playMode].currentFrame(); + } + + for( trackVector::iterator it = tv.begin(); it != tv.end(); + ++it ) + { + if( ( *it )->muted() == FALSE ) + { + ( *it )->play( m_playPos[m_playMode], + m_playPos[m_playMode].currentFrame(), + played_frames, total_frames_played, + tco_num ); + } + } + + // update frame-counters + total_frames_played += played_frames; + m_playPos[m_playMode].setCurrentFrame( + m_playPos[m_playMode].currentFrame() + + played_frames ); + m_playPos[m_playMode].setTact64th( + ( m_playPos[m_playMode].currentFrame() * + 64 / frames_per_tact) % 64 ); + } + + if( m_playPos[m_playMode].m_timeLine != NULL && + m_playPos[m_playMode].m_timeLineUpdate == TRUE && + m_exporting == FALSE ) + { + m_playPos[m_playMode].m_timeLine->updatePosition(); + } + + if( m_exporting == TRUE ) + { + tact tacts = lengthInTacts() + 1; + if( m_playPos[PLAY_SONG].getTact() >= tacts ) + { + // now pause the mixer - method + // exportProjectDialog::redrawProgressBar() which is + // called every 100 ms will find out that export + // is done and will act according to this + mixer::inst()->pause(); + } + else + { + m_epd->updateProgressBar( + ( m_playPos[PLAY_SONG].getTact() * 64 + + m_playPos[PLAY_SONG].getTact64th() ) * + 100 / ( tacts * 64 ) ); + } + } + + if( m_playMode == PLAY_PATTERN && m_loopPattern == FALSE && + m_patternToPlay->isFreezing() == TRUE && + m_playPos[PLAY_PATTERN] > m_patternToPlay->length() ) + { + m_patternToPlay->finishFreeze(); + } +} + + + + + + + + + +void songEditor::play( void ) +{ + if( m_playing == TRUE ) + { + if( m_playMode != PLAY_SONG ) + { + // make sure, bb-editor updates/resets it play-button + bbEditor::inst()->stop(); + //pianoRoll::inst()->stop(); + } + else + { + pause(); + return; + } + } +#ifdef QT4 + m_playButton->setIcon( embed::getIconPixmap( "pause" ) ); +#else + m_playButton->setPixmap( embed::getIconPixmap( "pause" ) ); +#endif + m_actions.push_back( ACT_PLAY_SONG ); +} + + + + +void songEditor::playTrack( track * _trackToPlay ) +{ + if( m_playing == TRUE ) + { + stop(); + } + m_trackToPlay = _trackToPlay; + + m_actions.push_back( ACT_PLAY_TRACK ); +} + + + + +void songEditor::playBB( void ) +{ + if( m_playing == TRUE ) + { + stop(); + } + m_actions.push_back( ACT_PLAY_BB ); +} + + + + +void songEditor::playPattern( pattern * _patternToPlay, bool _loop ) +{ + if( m_playing == TRUE ) + { + stop(); + } + m_patternToPlay = _patternToPlay; + m_loopPattern = _loop; + m_actions.push_back( ACT_PLAY_PATTERN ); +} + + + + +tact songEditor::lengthInTacts( void ) const +{ + tact len = 0; + constTrackVector ctv = tracks(); + for( constTrackVector::const_iterator it = ctv.begin(); it != ctv.end(); + ++it ) + { + len = tMax( ( *it )->length(), len ); + } + return( len ); +} + + + + +void songEditor::setPlayPos( tact _tact_num, tact64th _t_64th, playModes + _play_mode ) +{ + m_playPos[_play_mode].setTact( _tact_num ); + m_playPos[_play_mode].setTact64th( _t_64th ); + m_playPos[_play_mode].setCurrentFrame( static_cast( + _t_64th * framesPerTact() / 64.0f ) ); + if( _play_mode == m_playMode ) + { + updateTimeLinePosition(); + } +} + + + + +void songEditor::updateTimeLinePosition( void ) +{ + if( m_playPos[m_playMode].m_timeLine != NULL && + m_playPos[m_playMode].m_timeLineUpdate == TRUE ) + { + m_playPos[m_playMode].m_timeLine->updatePosition(); + } +} + + + + +void songEditor::stop( void ) +{ + m_actions.push_back( ACT_STOP_PLAY ); +#ifdef QT4 + m_playButton->setIcon( embed::getIconPixmap( "play" ) ); +#else + m_playButton->setPixmap( embed::getIconPixmap( "play" ) ); +#endif + m_scrollBack = TRUE; +} + + + + + + +void songEditor::pause( void ) +{ + m_actions.push_back( ACT_PAUSE ); +#ifdef QT4 + m_playButton->setIcon( embed::getIconPixmap( "play" ) ); +#else + m_playButton->setPixmap( embed::getIconPixmap( "play" ) ); +#endif +} + + + + +void songEditor::resumeFromPause( void ) +{ + m_actions.push_back( ACT_RESUME_FROM_PAUSE ); +#ifdef QT4 + m_playButton->setIcon( embed::getIconPixmap( "pause" ) ); +#else + m_playButton->setPixmap( embed::getIconPixmap( "pause" ) ); +#endif +} + + + + +void songEditor::startExport( void ) +{ + stop(); + doActions(); + + play(); + doActions(); + + m_exporting = TRUE; +} + + + + +void songEditor::stopExport( void ) +{ + stop(); + m_exporting = FALSE; + + // if we rendered file from cmd-line quit after export + if( file_to_render != "" ) + { + qApp->quit(); + } +} + + + + + + +void songEditor::insertTact( void ) +{ + trackVector tv = tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + ( *it )->getTrackContentWidget()->insertTact( + m_playPos[PLAY_SONG] ); + } +} + + + + +void songEditor::removeTact( void ) +{ + trackVector tv = tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + ( *it )->getTrackContentWidget()->removeTact( + m_playPos[PLAY_SONG] ); + } +} + + + + +void songEditor::addChannelTrack( void ) +{ + channelTrack * t = dynamic_cast< channelTrack * >( + track::createTrack( track::CHANNEL_TRACK, this ) ); +#ifdef LMMS_DEBUG + assert( t != NULL ); +#endif + t->loadPluginSettings( tripleOscillator::defaultSettings() ); + t->toggledChannelButton( TRUE ); + t->show(); +} + + + + +void songEditor::addBBTrack( void ) +{ + track * t = track::createTrack( track::BB_TRACK, this ); + if( dynamic_cast( t ) != NULL ) + { + dynamic_cast( t )->clickedTrackLabel(); + } +} + + + + +void songEditor::addSampleTrack( void ) +{ + (void) track::createTrack( track::SAMPLE_TRACK, this ); +} + + + + +float songEditor::framesPerTact( void ) const +{ + return( mixer::inst()->sampleRate() * 60.0f * MAIN_BEATS_PER_TACT / + m_bpmSpinBox->value() ); +} + + + + + +bool songEditor::mayChangeProject( void ) +{ + if( m_modified == FALSE ) + { + return( TRUE ); + } + +/* int answer = QMessageBox:: +#if QT_VERSION >= 0x030200 + question +#else + information +#endif + ( lmmsMainWin::inst(), + tr( "Project not saved" ), + tr( "The current project was " + "modified since last " + "saving. Do you want " + "to save it now?" ), + QMessageBox::Yes, + QMessageBox::No, + QMessageBox::Cancel );*/ + QMessageBox mb ( tr( "Project not saved" ), + tr( "The current project was modified since " + "last saving. Do you want to save it " + "now?" ), +#if QT_VERSION >= 0x030200 + QMessageBox::Question, +#else + QMessageBox::Information, +#endif + QMessageBox::Yes, + QMessageBox::No, + QMessageBox::Cancel, + lmmsMainWin::inst() ); + int answer = mb.exec(); + + if( answer == QMessageBox::Yes ) + { + return( lmmsMainWin::inst()->saveProject() ); + } + else if( answer == QMessageBox::No ) + { + return( TRUE ); + } + + return( FALSE ); +} + + + + +void songEditor::clearProject( void ) +{ + if( m_playing ) + { + // stop play, because it's dangerous that play-routines try to + // access non existing data (as you can see in the next lines, + // all data is cleared!) + stop(); + doActions(); + } + + // make sure all running notes are cleared, otherwise the whole + // thing will end up in a SIGSEGV... + //mixer::inst()->clear(); + while( mixer::inst()->haveNoRunningNotes() == FALSE ) + { + } + + trackVector tv = tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + removeTrack( *it ); + } + tv = bbEditor::inst()->tracks(); + for( trackVector::iterator it = tv.begin(); it != tv.end(); ++it ) + { + bbEditor::inst()->removeTrack( *it ); + } + m_projectNotes->clear(); +} + + + + + +// create new file +void songEditor::createNewProject( void ) +{ + clearProject(); + + track * t; + t = track::createTrack( track::CHANNEL_TRACK, this ); + dynamic_cast< channelTrack * >( t )->loadPluginSettings( + tripleOscillator::defaultSettings() ); + track::createTrack( track::SAMPLE_TRACK, this ); + t = track::createTrack( track::CHANNEL_TRACK, bbEditor::inst() ); + dynamic_cast< channelTrack * >( t )->loadPluginSettings( + tripleOscillator::defaultSettings() ); + track::createTrack( track::BB_TRACK, this ); + + setBPM( DEFAULT_BPM ); + m_masterVolumeSlider->setValue( 100 ); + m_masterPitchSlider->setValue( 0 ); + + m_fileName = m_oldFileName = ""; + + m_modified = FALSE; + + lmmsMainWin::inst()->resetWindowTitle( "" ); +} + + + + +void FASTCALL songEditor::createNewProjectFromTemplate( const QString & + _template ) +{ + loadProject( _template ); + // clear file-name so that user doesn't overwrite template when + // saving... + m_fileName = m_oldFileName = ""; +} + + + + +// load given song +void FASTCALL songEditor::loadProject( const QString & _file_name ) +{ + + clearProject(); + m_fileName = _file_name; + m_oldFileName = _file_name; + + multimediaProject mmp( m_fileName ); + // if file could not be opened, head-node is null and we create + // new project + if( mmp.head().isNull() ) + { + createNewProject(); + return; + } + + // get the header information from the DOM + QDomNode node = mmp.head().firstChild(); + while( !node.isNull() ) + { + if( node.isElement() ) + { + if( node.nodeName() == "bpm" && + node.toElement().attribute( "value" ).toInt() > 0 ) + { + setBPM( node.toElement().attribute( "value" + ).toInt() ); + } + else if( node.nodeName() == "mastervol" ) + { + if( node.toElement().attribute( "value" + ).toInt() > 0 ) + { + m_masterVolumeSlider->setValue( 200 - + node.toElement().attribute( "value" ).toInt() ); + } + else + { + m_masterVolumeSlider->setValue( + DEFAULT_VOLUME ); + } + } + else if( node.nodeName() == "masterpitch" ) + { + m_masterPitchSlider->setValue( + node.toElement().attribute( "value" + ).toInt() ); + } + } + node = node.nextSibling(); + } + + node = mmp.content().firstChild(); + while( !node.isNull() ) + { + if( node.isElement() ) + { + if( node.nodeName() == "trackcontainer" ) + { + loadSettings( node.toElement() ); + } + else if( node.nodeName() == m_projectNotes->nodeName() ) + { + m_projectNotes->loadSettings( + node.toElement() ); + } + } + node = node.nextSibling(); + } + + m_modified = FALSE; + m_leftRightScroll->setValue( 0 ); + + lmmsMainWin::inst()->resetWindowTitle( "" ); +} + + + + +// save current song +bool songEditor::saveProject( void ) +{ + multimediaProject mmp( multimediaProject::SONG_PROJECT ); + + QDomElement bpm = mmp.createElement( "bpm" ); + bpm.setAttribute( "value", QString::number( m_bpmSpinBox->value() ) ); + mmp.head().appendChild( bpm ); + + QDomElement mv = mmp.createElement( "mastervol" ); + mv.setAttribute( "value", QString::number( 200 - + m_masterVolumeSlider->value() ) ); + mmp.head().appendChild( mv ); + + QDomElement mp = mmp.createElement( "masterpitch" ); + mp.setAttribute( "value", QString::number( + m_masterPitchSlider->value() ) ); + mmp.head().appendChild( mp ); + + + saveSettings( mmp, mmp.content() ); + m_projectNotes->saveSettings( mmp, mmp.content() ); + + if( mmp.writeFile( m_fileName, m_oldFileName == "" || + m_fileName != m_oldFileName ) == TRUE ) + { + m_modified = FALSE; + + lmmsMainWin::inst()->statusBar()->showMessage( + tr( "%1 saved." ).arg( m_fileName ), + 3000 ); + lmmsMainWin::inst()->resetWindowTitle( "" ); + } + else + { + lmmsMainWin::inst()->statusBar()->showMessage( + tr( "Project NOT saved." ), 3000 ); + return( FALSE ); + } + return( TRUE ); +} + + + + +// save current song in given filename +bool FASTCALL songEditor::saveProjectAs( const QString & _file_name ) +{ + QString o = m_oldFileName; + m_oldFileName = m_fileName; + m_fileName = _file_name; + if( saveProject() == FALSE ) + { + m_fileName = m_oldFileName; + m_oldFileName = o; + return( FALSE ); + } + m_oldFileName = m_fileName; + return( TRUE ); +} + + + +void songEditor::importProject( void ) +{ +#ifdef QT4 + QFileDialog ofd( this, tr( "Import file" ), "", + tr( "MIDI-files (*.mid)" ) ); +#else + QFileDialog ofd( QString::null, + tr( "MIDI-files (*.mid)" ), + this, "", TRUE ); + ofd.setWindowTitle( tr( "Import file" ) ); +#endif + ofd.setDirectory( configManager::inst()->projectsDir() ); + ofd.setFileMode( QFileDialog::ExistingFiles ); + if( ofd.exec () == QDialog::Accepted && + !ofd.selectedFiles().isEmpty() ) + { + midiFile mf( ofd.selectedFiles()[0] ); + mf.importToTrackContainer( this ); + } +} + + + + +void songEditor::exportProject( void ) +{ + QString base_filename; + + if( m_fileName != "" ) + { +#ifdef QT4 + base_filename = QFileInfo( m_fileName ).absolutePath() + "/" + + QFileInfo( m_fileName + ).completeBaseName(); +#else + base_filename = QFileInfo( m_fileName ).dirPath() + "/" + + QFileInfo( m_fileName ).baseName( + TRUE ); +#endif + } + else + { + base_filename = tr( "untitled" ); + } + base_filename += fileEncodeDevices[0].m_extension; + + QFileDialog efd( lmmsMainWin::inst() ); + efd.setFileMode( QFileDialog::AnyFile ); + + int idx = 0; +#ifdef QT4 + QStringList types; + while( fileEncodeDevices[idx].m_fileType != NULL_FILE ) + { + types << tr( fileEncodeDevices[idx].m_description ); + ++idx; + } + efd.setFilters( types ); + efd.selectFile( base_filename ); +#else + while( fileEncodeDevices[idx].m_fileType != NULL_FILE ) + { + efd.addFilter( tr( fileEncodeDevices[idx].m_description ) ); + ++idx; + } + efd.setSelectedFilter( tr( fileEncodeDevices[0].m_description ) ); + efd.setSelection( base_filename ); +#endif + efd.setWindowTitle( tr( "Select file for project-export..." ) ); + + if( efd.exec() == QDialog::Accepted && +#ifdef QT4 + !efd.selectedFiles().isEmpty() && efd.selectedFiles()[0] != "" +#else + efd.selectedFile() != "" +#endif + ) + { +#ifdef QT4 + const QString export_file_name = efd.selectedFiles()[0]; +#else + const QString export_file_name = efd.selectedFile(); +#endif + if( QFileInfo( export_file_name ).exists() == TRUE && + QMessageBox::warning( lmmsMainWin::inst(), + tr( "File already exists" ), + tr( "The file \"%1\" already " + "exists. Do you want " + "to overwrite it?" + ).arg( QFileInfo( + export_file_name ).fileName() ), + QMessageBox::Yes, + QMessageBox::No | + QMessageBox::Escape | + QMessageBox::Default ) + == QMessageBox::No ) + { + return; + } + + m_epd = new exportProjectDialog( export_file_name, + lmmsMainWin::inst() ); + m_epd->exec(); + delete m_epd; + m_epd = NULL; + } +} + + + + + + + +void songEditor::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + trackContainer::saveSettings( _doc, _parent ); +} + + + + +void songEditor::loadSettings( const QDomElement & _this ) +{ + trackContainer::loadSettings( _this ); +} + + + +#include "song_editor.moc" + diff --git a/src/core/surround_area.cpp b/src/core/surround_area.cpp new file mode 100644 index 000000000..03f6e163c --- /dev/null +++ b/src/core/surround_area.cpp @@ -0,0 +1,242 @@ +/* + * surround_area.cpp - a widget for setting position of a channel + + * calculation of volume for each speaker + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +#include + +#include "surround_area.h" +#include "embed.h" +#include "templates.h" +#include "tooltip.h" + + + +const QPoint surroundArea::s_defaultSpeakerPositions[SURROUND_CHANNELS] = +{ + QPoint( -SURROUND_AREA_SIZE, -SURROUND_AREA_SIZE ), + QPoint( SURROUND_AREA_SIZE, -SURROUND_AREA_SIZE) +#ifndef DISABLE_SURROUND +, + QPoint( -SURROUND_AREA_SIZE, SURROUND_AREA_SIZE ), + QPoint( SURROUND_AREA_SIZE, SURROUND_AREA_SIZE ) +#endif +} ; + +QPixmap * surroundArea::s_backgroundArtwork = NULL; + + + + +surroundArea::surroundArea( QWidget * _parent ) : + QWidget( _parent ), + m_sndSrcPos( QPoint() ) +{ + if( s_backgroundArtwork == NULL ) + { + s_backgroundArtwork = new QPixmap( embed::getIconPixmap( + "surround_area" ) ); + } + + setFixedSize( s_backgroundArtwork->width(), + s_backgroundArtwork->height() ); +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + toolTip::add( this, + tr( "click to where this channel should be audible" ) ); +} + + + + +surroundArea::~surroundArea() +{ +} + + + + +volumeVector surroundArea::getVolumeVector( float _v_scale ) const +{ + volumeVector v; + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; ++chnl ) + { + v.vol[chnl] = getVolume( s_defaultSpeakerPositions[chnl], + _v_scale ); + } + + return( v ); +} + + + + +void surroundArea::setValue( const QPoint & _p ) +{ + if( tLimit( _p.x(), -SURROUND_AREA_SIZE, SURROUND_AREA_SIZE ) != + _p.x() || + tLimit( _p.y(), -SURROUND_AREA_SIZE, SURROUND_AREA_SIZE ) != _p.y() ) + { + m_sndSrcPos = QPoint( 0, 0 ); + } + else + { + m_sndSrcPos = _p; + } + update(); +} + + + + +FASTCALL float surroundArea::getVolume( const QPoint & _speaker_pos, + float _v_scale ) const +{ + const int x = _speaker_pos.x() - m_sndSrcPos.x(); + const int y = _speaker_pos.y() - m_sndSrcPos.y(); + const float new_vol = 2.0f - sqrt( x*x + y*y ) * + ( 1.0f / SURROUND_AREA_SIZE ); + + return( tLimit( new_vol, 0.0f, 1.0f ) * _v_scale ); +} + + + + +void surroundArea::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); + if( s_backgroundArtwork->size() != size() ) + { + p.drawPixmap( 0, 0, *s_backgroundArtwork ); + } + else + { + p.drawPixmap( 0, 0, s_backgroundArtwork->scaled( + width(), height(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation ) ); + } +#else + QPixmap pm; + if( s_backgroundArtwork->size() != size() ) + { + pm.convertFromImage( + s_backgroundArtwork->convertToImage().smoothScale( + width(), height() ) ); + } + else + { + pm = *s_backgroundArtwork; + } + QPainter p( &pm ); +#endif + const int x = ( width() + m_sndSrcPos.x() * ( width() - 4 ) / + SURROUND_AREA_SIZE ) / 2; + const int y = ( height() + m_sndSrcPos.y() * ( height() - 4 ) / + SURROUND_AREA_SIZE ) / 2; + p.setPen( QColor( 64, 255, 64 ) ); + p.drawPoint( x, y - 1 ); + p.drawPoint( x - 1, y ); + p.drawPoint( x, y ); + p.drawPoint( x + 1, y ); + p.drawPoint( x, y + 1 ); + +#ifndef QT4 + // blit drawn pixmap to actual widget + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void surroundArea::mousePressEvent( QMouseEvent * _me ) +{ + const int w = width();//s_backgroundArtwork->width(); + const int h = height();//s_backgroundArtwork->height(); + if( _me->x() > 1 && _me->x() < w-1 && _me->y() > 1 && _me->y() < h-1 ) + { + m_sndSrcPos.setX( ( _me->x() * 2 - w ) * SURROUND_AREA_SIZE / + ( w - 4 ) ); + m_sndSrcPos.setY( ( _me->y() * 2 - h ) * SURROUND_AREA_SIZE / + ( h - 4 ) ); + update(); + if( _me->button() != Qt::NoButton ) + { + QApplication::setOverrideCursor( Qt::BlankCursor ); + } + } + else + { + int x = tLimit( _me->x(), 2, w - 2 ); + int y = tLimit( _me->y(), 2, h - 2 ); + QCursor::setPos( mapToGlobal( QPoint( x, y ) ) ); + } + + emit valueChanged( m_sndSrcPos ); +} + + + + +void surroundArea::mouseMoveEvent( QMouseEvent * _me ) +{ + mousePressEvent( _me ); +} + + + + +void surroundArea::mouseReleaseEvent( QMouseEvent * ) +{ + QApplication::restoreOverrideCursor(); +} + + + + +#include "surround_area.moc" + diff --git a/src/core/timeline.cpp b/src/core/timeline.cpp new file mode 100644 index 000000000..cc6999ad8 --- /dev/null +++ b/src/core/timeline.cpp @@ -0,0 +1,328 @@ +/* + * playpos_marker.cpp - class timeLine, representing a time-line with + * position marker + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include + +#endif + + +#include "timeline.h" +#include "nstate_button.h" +#include "embed.h" +#include "templates.h" + + + +QPixmap * timeLine::s_timeLinePixmap = NULL; +QPixmap * timeLine::s_posMarkerPixmap = NULL; +QPixmap * timeLine::s_loopPointPixmap = NULL; + + +timeLine::timeLine( const int _xoff, const int _yoff, const float _ppt, + songEditor::playPos & _pos, const midiTime & _begin, + QWidget * _parent ) : + QWidget( _parent ), + m_xOffset( _xoff ), + m_posMarkerX( 0 ), + m_ppt( _ppt ), + m_pos( _pos ), + m_begin( _begin ), + m_savedPos( -1 ), + m_action( NONE ), + m_moveXOff( 0 ) +{ + m_loopPos[0] = 0; + m_loopPos[1] = 64; + + if( s_timeLinePixmap == NULL) + { + s_timeLinePixmap = new QPixmap( embed::getIconPixmap( + "timeline" ) ); + } + if( s_posMarkerPixmap == NULL) + { + s_posMarkerPixmap = new QPixmap( embed::getIconPixmap( + "playpos_marker" ) ); + } + if( s_loopPointPixmap == NULL) + { + s_loopPointPixmap = new QPixmap( embed::getIconPixmap( + "loop_point" ) ); + } + + move( 0, _yoff ); + setFixedHeight( s_timeLinePixmap->height() ); + + m_xOffset -= s_posMarkerPixmap->width() / 2; + + + m_autoScroll = new nStateButton( this ); + m_autoScroll->move( 3, 3 ); + m_autoScroll->setGeneralToolTip( tr( "Enable/disable " + "auto-scrolling" ) ); + m_autoScroll->addState( embed::getIconPixmap( "autoscroll_on" ) ); + m_autoScroll->addState( embed::getIconPixmap( "autoscroll_off" ) ); + + m_loopPoints = new nStateButton( this ); + m_loopPoints->move( 20, 3 ); + m_loopPoints->setGeneralToolTip( tr( "Enable/disable loop-points" ) ); + m_loopPoints->addState( embed::getIconPixmap( "loop_points_off" ) ); + m_loopPoints->addState( embed::getIconPixmap( "loop_points_on" ) ); + connect( m_loopPoints, SIGNAL( stateChanged( int ) ), this, + SLOT( toggleLoopPoints( int ) ) ); + + m_behaviourAtStop = new nStateButton( this ); + m_behaviourAtStop ->move( 37, 3 ); + m_behaviourAtStop ->addState( embed::getIconPixmap( "back_to_zero" ), + tr( "After stopping go back to begin" ) + ); + m_behaviourAtStop ->addState( embed::getIconPixmap( + "back_to_start" ), + tr( "After stopping go back to " + "position at which playing was " + "started" ) ); + m_behaviourAtStop ->addState( embed::getIconPixmap( + "keep_stop_position" ), + tr( "After stopping keep position" ) ); + +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + + m_pos.m_timeLine = this; + + updatePosition(); +} + + + + +timeLine::~timeLine() +{ + m_pos.m_timeLine = NULL; +} + + + + +timeLine::behaviourAtStopStates timeLine::behaviourAtStop( void ) const +{ + return( static_cast( + m_behaviourAtStop->state() ) ); +} + + + + +bool timeLine::loopPointsEnabled( void ) const +{ + return( m_loopPoints->state() == LOOP_POINTS_ENABLED ); +} + + + + +void timeLine::updatePosition( const midiTime & ) +{ + const int new_x = markerX( m_pos ); + + if( new_x != m_posMarkerX ) + { + m_posMarkerX = new_x; +#ifndef QT4 + qApp->lock(); +#endif + update(); +#ifndef QT4 + qApp->unlock(); +#endif + if( m_autoScroll->state() == AUTOSCROLL_ENABLED ) + { + emit positionChanged( m_pos ); + } + } +} + + + + +void timeLine::toggleLoopPoints( int _n ) +{ + update(); +} + + + + +void timeLine::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap draw_pm( rect().size() ); + + QPainter p( &draw_pm, this ); +#endif + + for( int x = 0; x < width(); x += s_timeLinePixmap->width() ) + { + p.drawPixmap( x, 0, *s_timeLinePixmap ); + } + p.setClipRect( m_xOffset, 0, width() - m_xOffset, height() ); + p.setPen( QColor( 0, 0, 0 ) ); + + if( m_loopPoints->state() == LOOP_POINTS_ENABLED ) + { + p.drawPixmap( markerX( m_loopPos[0] ), 7, + *s_loopPointPixmap ); + p.drawPixmap( markerX( m_loopPos[1] ), 7, + *s_loopPointPixmap ); + } + + + tact tact_num = m_begin.getTact(); + int x = m_xOffset + s_posMarkerPixmap->width() / 2 - + ( ( static_cast( m_begin * m_ppt ) / 64 ) % + static_cast( m_ppt ) ); + + for( int i = 0; x + i * m_ppt < width(); ++i ) + { + ++tact_num; + if( ( tact_num - 1 ) % + tMax( 1, static_cast( 64.0f / m_ppt ) ) == 0 ) + { + p.drawText( x + static_cast( i * m_ppt ), 16, + QString::number( tact_num ) ); + } + } + + p.drawPixmap( m_posMarkerX, 4, *s_posMarkerPixmap ); + +#ifndef QT4 + // and blit all the drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + + +void timeLine::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->x() < m_xOffset ) + { + return; + } + if( _me->button() == Qt::RightButton ) + { + if( m_loopPoints->state() != LOOP_POINTS_ENABLED ) + { + return; + } + if( _me->x() >= markerX( loopBegin() ) && + _me->x() <= markerX( loopBegin() ) + + s_loopPointPixmap->width() ) + { + m_action = MOVE_LOOP_BEGIN; + m_moveXOff = s_loopPointPixmap->width() / 2; + } + else if( _me->x() >= markerX( loopEnd() ) && + _me->x() <= markerX( loopEnd() ) + + s_loopPointPixmap->width() ) + { + m_action = MOVE_LOOP_END; + m_moveXOff = s_loopPointPixmap->width() / 2; + } + } + else + { + m_action = MOVE_POS_MARKER; + if( _me->x() - m_xOffset < s_posMarkerPixmap->width() ) + { + m_moveXOff = _me->x() - m_xOffset; + } + else + { + m_moveXOff = s_posMarkerPixmap->width() / 2; + } + } + mouseMoveEvent( _me ); +} + + + + +void timeLine::mouseMoveEvent( QMouseEvent * _me ) +{ + const midiTime t = m_begin + static_cast( tMax( _me->x() - + m_xOffset - m_moveXOff, 0 ) * 64 / m_ppt ); + switch( m_action ) + { + case MOVE_POS_MARKER: + m_pos.setTact( t.getTact() ); + m_pos.setTact64th( t.getTact64th() ); + m_pos.setCurrentFrame( 0 ); + updatePosition(); + break; + + case MOVE_LOOP_BEGIN: + m_loopPos[0] = t; + update(); + break; + + case MOVE_LOOP_END: + m_loopPos[1] = t; + update(); + break; + + default: + break; + } +} + + + + +void timeLine::mouseReleaseEvent( QMouseEvent * _me ) +{ + m_action = NONE; +} + + + + +#include "timeline.moc" + diff --git a/src/core/track.cpp b/src/core/track.cpp new file mode 100644 index 000000000..40b4b2740 --- /dev/null +++ b/src/core/track.cpp @@ -0,0 +1,1087 @@ +/* + * track.cpp - implementation of classes concerning tracks -> neccessary for + * all track-like objects (beat/bassline, sample-track...) + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +#include "track.h" +#include "track_container.h" +#include "channel_track.h" +#include "bb_track.h" +#include "sample_track.h" +#include "song_editor.h" +#include "templates.h" +#include "clipboard.h" +#include "embed.h" +#include "pixmap_button.h" +#include "debug.h" +#include "tooltip.h" + + + +const Sint16 RESIZE_GRIP_WIDTH = 4; + +const Uint16 TRACK_OP_BTN_WIDTH = 20; +const Uint16 TRACK_OP_BTN_HEIGHT = 14; + + + + +// =========================================================================== +// trackContentObject +// =========================================================================== +trackContentObject::trackContentObject( track * _track ) : + QWidget( _track->getTrackContentWidget() +#ifndef QT4 + , NULL, Qt::WDestructiveClose +#endif + ), + m_track( _track ), + m_startPosition(), + m_length(), + m_moving( FALSE ), + m_resizing( FALSE ), + m_autoResize( FALSE ), + m_initialMouseX( 0 ) +{ +#ifdef QT4 + setAttribute( Qt::WA_DeleteOnClose ); + setFocusPolicy( Qt::StrongFocus ); +#else + setFocusPolicy( StrongFocus ); +#endif + show(); + movePosition( 0 ); + changeLength( 0 ); + setFixedHeight( parentWidget()->height()-2 ); +// if( useFixedWidth() ) +// setFixedWidth( _channel_track->getTrackContentWidget()->width() ); +} + + + + +trackContentObject::~trackContentObject() +{ +} + + + + +bool trackContentObject::fixedTCOs( void ) +{ + return( m_track->getTrackContainer()->fixedTCOs() ); +} + + + + +void trackContentObject::movePosition( const midiTime & _pos ) +{ + if( m_startPosition != _pos ) + { + songEditor::inst()->setModified(); + } + m_startPosition = _pos; + m_track->getTrackWidget()->changePosition(); + // moving of TCO can result in change of song-length etc., + // therefore we update the trackcontainer + m_track->getTrackContainer()->update(); + setMouseTracking( TRUE ); +} + + + + +void trackContentObject::changeLength( const midiTime & _length ) +{ + if( m_length != _length ) + { + songEditor::inst()->setModified(); + } + m_length = _length; + setFixedWidth( static_cast( m_length * pixelsPerTact() / 64 ) + + TCO_BORDER_WIDTH*2 ); + // changing length of TCO can result in change of song-length etc., + // therefore we update the trackcontainer + m_track->getTrackContainer()->update(); +} + + + + +float trackContentObject::pixelsPerTact( void ) +{ + if( fixedTCOs() ) + { + return( getTrack()->getTrackContentWidget()->width() - + 2 * TCO_BORDER_WIDTH ) / + tMax( length().getTact(), 1.0f ); + } + return( getTrack()->getTrackContainer()->pixelsPerTact() ); +} + + + + + +void trackContentObject::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton && fixedTCOs() == FALSE ) + { + m_initialMouseX = _me->x() - TCO_BORDER_WIDTH; + + if( _me->x() < width()-RESIZE_GRIP_WIDTH ) + { + m_moving = TRUE; + QCursor c( Qt::SizeAllCursor ); + QApplication::setOverrideCursor( c ); + } + else if( m_autoResize == FALSE ) + { + m_resizing = TRUE; + QCursor c( Qt::SizeHorCursor ); + QApplication::setOverrideCursor( c ); + } + } +} + + + + +void trackContentObject::mouseMoveEvent( QMouseEvent * _me ) +{ + const float ppt = m_track->getTrackContainer()->pixelsPerTact(); + if( m_moving ) + { + int x = mapToParent( _me->pos() ).x() - m_initialMouseX; + movePosition( tMax( 0, (Sint32) m_track->getTrackContainer()-> + currentPosition() + + static_cast( x * 64 / ppt ) ) ); + m_track->getTrackWidget()->changePosition(); + } + else if( m_resizing ) + { + changeLength( tMax( 64, + static_cast( _me->x() * 64 / ppt ) ) ); + } + else + { + if( _me->x() >= width()-RESIZE_GRIP_WIDTH ) + { + if( QApplication::overrideCursor() != NULL && + QApplication::overrideCursor()->shape() != + Qt::SizeHorCursor ) + { + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + } + QCursor c( Qt::SizeHorCursor ); + QApplication::setOverrideCursor( c ); + } + else + { + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + } + } +} + + + + +void trackContentObject::mouseReleaseEvent( QMouseEvent * _me ) +{ + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } + m_moving = FALSE; + m_resizing = FALSE; +} + + + + +void trackContentObject::contextMenuEvent( QContextMenuEvent * _cme ) +{ + QMenu contextMenu( this ); + if( fixedTCOs() == FALSE ) + { + contextMenu.addAction( embed::getIconPixmap( "cancel" ), + tr( "Delete" ), this, SLOT( close() ) ); +#ifdef QT4 + contextMenu.addSeparator(); +#else + contextMenu.insertSeparator(); +#endif + contextMenu.addAction( embed::getIconPixmap( "edit_cut" ), + tr( "Cut" ), this, SLOT( cut() ) ); + } + contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), + tr( "Copy" ), this, SLOT( copy() ) ); + contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), + tr( "Paste" ), this, SLOT( paste() ) ); + //contextMenu.insertSeparator(); + //contextMenu.insertItem( tr( "&Help" ), this, SLOT( displayHelp() ) ); + + constructContextMenu( &contextMenu ); + + contextMenu.exec( QCursor::pos() ); +} + + + + +void trackContentObject::close( void ) +{ + m_track->getTrackContentWidget()->removeTCO( this, FALSE ); + // we have to give our track-container the focus because otherwise the + // op-buttons of our track-widgets could become focus and when the user + // presses space for playing song, just one of these buttons is pressed + // which results in unwanted effects + m_track->getTrackContainer()->setFocus(); + QWidget::close(); +} + + + + +void trackContentObject::cut( void ) +{ + copy(); + close(); +} + + + + +void trackContentObject::copy( void ) +{ + clipboard::copy( this ); +} + + + + +void trackContentObject::paste( void ) +{ + if( clipboard::getContent( nodeName() ) != NULL ) + { + loadSettings( *( clipboard::getContent( nodeName() ) ) ); + } +} + + + + +void trackContentObject::setAutoResizeEnabled( bool _e ) +{ + m_autoResize = _e; +} + + + + +// =========================================================================== +// trackContentWidget +// =========================================================================== +trackContentWidget::trackContentWidget( trackWidget * _parent ) : + QWidget( _parent ), + m_trackWidget( _parent ) +{ +#ifdef QT4 + QPalette pal; + pal.setColor( backgroundRole(), QColor( 96, 96, 96 ) ); + setPalette( pal ); +#else + setPaletteBackgroundColor( QColor( 96, 96, 96 ) ); +#endif + setMouseTracking( TRUE ); +} + + + + +trackContentWidget::~trackContentWidget() +{ +} + + + + +trackContentObject * FASTCALL trackContentWidget::getTCO( csize _tco_num ) +{ + if( _tco_num < m_trackContentObjects.size() ) + { + return( m_trackContentObjects[_tco_num] ); + } + printf( "called trackContentWidget::getTCO( %d, TRUE ), " + " but TCO %d doesn't exist\n", _tco_num, _tco_num ); + //m_trackWidget->getTrack().addTCO( + // m_trackWidget->getTrack().createTCO() ); + return( NULL ); +} + + + + +csize trackContentWidget::numOfTCOs( void ) +{ + return( m_trackContentObjects.size() ); +} + + + + +trackContentObject * FASTCALL trackContentWidget::addTCO( + trackContentObject * _tco ) +{ + m_trackContentObjects.push_back( _tco ); + _tco->move( 0, 1 );//(m_trackWidget->height()-_tco->height())/2 ); + m_trackWidget->changePosition(); + songEditor::inst()->setModified(); + return( _tco ); // just for convenience +} + + + + +void FASTCALL trackContentWidget::removeTCO( csize _tco_num, bool _also_delete ) +{ + removeTCO( getTCO( _tco_num ), _also_delete ); +} + + + + +void trackContentWidget::removeTCO( trackContentObject * _tco, + bool _also_delete ) +{ + tcoVector::iterator it = qFind( m_trackContentObjects.begin(), + m_trackContentObjects.end(), + _tco ); + if( it != m_trackContentObjects.end() ) + { + if( _also_delete ) + { + delete _tco; + } + m_trackContentObjects.erase( it ); + songEditor::inst()->setModified(); + } +} + + + + +void trackContentWidget::removeAllTCOs( void ) +{ + while( !m_trackContentObjects.isEmpty() ) + { + delete m_trackContentObjects.first(); + m_trackContentObjects.erase( m_trackContentObjects.begin() ); + } +} + + + + +void trackContentWidget::swapPositionOfTCOs( csize _tco_num1, csize _tco_num2 ) +{ + // TODO: range-checking + qSwap( m_trackContentObjects[_tco_num1], + m_trackContentObjects[_tco_num2] ); + + const midiTime pos = m_trackContentObjects[_tco_num1]->startPosition(); + + m_trackContentObjects[_tco_num1]->movePosition( + m_trackContentObjects[_tco_num2]->startPosition() ); + m_trackContentObjects[_tco_num2]->movePosition( pos ); +} + + + + +tact trackContentWidget::length( void ) const +{ + // find last end-position + midiTime last = 0; + for( tcoVector::const_iterator it = m_trackContentObjects.begin(); + it != m_trackContentObjects.end(); ++it ) + { + last = tMax( ( *it )->endPosition(), last ); + } + return( last.getTact() + ( ( last.getTact64th() != 0 )? 1 : 0 ) ); +} + + + + +void trackContentWidget::insertTact( const midiTime & _pos ) +{ + // we'll increase the position of every TCO, posated behind _pos, by + // one tact + for( tcoVector::iterator it = m_trackContentObjects.begin(); + it != m_trackContentObjects.end(); ++it ) + { + if( ( *it )->startPosition() >= _pos ) + { + ( *it )->movePosition( (*it)->startPosition() + 64 ); + } + } +} + + + + +void trackContentWidget::removeTact( const midiTime & _pos ) +{ + // we'll decrease the position of every TCO, posated behind _pos, by + // one tact + for( tcoVector::iterator it = m_trackContentObjects.begin(); + it != m_trackContentObjects.end(); ++it ) + { + if( ( *it )->startPosition() >= _pos ) + { + ( *it )->movePosition( tMax( ( *it )->startPosition() - + 64, 0 ) ); + } + } +} + + + + +void trackContentWidget::updateTCOs( void ) +{ + for( tcoVector::iterator it = m_trackContentObjects.begin(); + it != m_trackContentObjects.end(); ++it ) + { + ( *it )->update(); + } +} + + + + +void trackContentWidget::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton && + m_trackWidget->getTrack()->getTrackContainer()->fixedTCOs() == + FALSE ) + { + const midiTime position = midiTime( m_trackWidget->getTrack()-> + getTrackContainer()-> + currentPosition() + _me->x() * + 64 / + static_cast( + m_trackWidget-> + getTrack()-> + getTrackContainer()-> + pixelsPerTact() + ) ); + trackContentObject * tco = addTCO( m_trackWidget->getTrack()-> + createTCO( position ) ); + tco->movePosition( position ); + } +} + + + + +void trackContentWidget::mouseMoveEvent( QMouseEvent * _me ) +{ + // if user moved TCO out of visible area, TCO doesn't receive + // mouse-events, so we have to do it here + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } +} + + + + +void trackContentWidget::mouseReleaseEvent( QMouseEvent * _me ) +{ + // if user moved TCO out of visible area, TCO doesn't receive + // mouseRelease-events... + while( QApplication::overrideCursor() != NULL ) + { + QApplication::restoreOverrideCursor(); + } +} + + + + +void trackContentWidget::resizeEvent( QResizeEvent * _re ) +{ + updateTCOs(); +} + + + + + +// =========================================================================== +// trackWidget +// =========================================================================== + +trackWidget::trackWidget( track * _track, QWidget * _parent ) : + QWidget( _parent ), + m_track( _track ), + m_trackOperationsWidget( this ), + m_trackSettingsWidget( this ), + m_trackContentWidget( this ) +{ +#ifdef QT4 + { + QPalette pal; + pal.setColor( m_trackOperationsWidget.backgroundRole(), + QColor( 128, 128, 128 ) ); + m_trackOperationsWidget.setPalette( pal ); + } +#else + m_trackOperationsWidget.setPaletteBackgroundColor( + QColor( 128, 128, 128 ) ); +#endif + + + QPushButton * clntr_btn = new QPushButton( embed::getIconPixmap( + "pr_edit_copy", 12, 12 ), + "", + &m_trackOperationsWidget ); + clntr_btn->setGeometry( 1, 1, TRACK_OP_BTN_WIDTH, TRACK_OP_BTN_HEIGHT ); + connect( clntr_btn, SIGNAL( clicked() ), this, SLOT( cloneTrack() ) ); + connect( clntr_btn, SIGNAL( clicked() ), clntr_btn, + SLOT( clearFocus() ) ); + toolTip::add( clntr_btn, tr( "Clone this track" ) ); + + QPushButton * deltr_btn = new QPushButton( embed::getIconPixmap( + "cancel", 12, 12 ), + "", &m_trackOperationsWidget ); + deltr_btn->setGeometry( 1, 1+TRACK_OP_BTN_HEIGHT, TRACK_OP_BTN_WIDTH, + TRACK_OP_BTN_HEIGHT ); + connect( deltr_btn, SIGNAL( clicked() ), this, SLOT( deleteTrack() ) ); + connect( deltr_btn, SIGNAL( clicked() ), deltr_btn, + SLOT( clearFocus() ) ); + toolTip::add( deltr_btn, tr( "Delete this track" ) ); + + QPushButton * muptr_btn = new QPushButton( embed::getIconPixmap( + "arp_up_on", 12, 12 ), + "", &m_trackOperationsWidget ); + muptr_btn->setGeometry( 1+TRACK_OP_BTN_WIDTH, 1, TRACK_OP_BTN_WIDTH, + TRACK_OP_BTN_HEIGHT ); + connect( muptr_btn, SIGNAL( clicked() ), this, SLOT( moveTrackUp() ) ); + connect( muptr_btn, SIGNAL( clicked() ), muptr_btn, + SLOT( clearFocus() ) ); + toolTip::add( muptr_btn, tr( "Move this track up" ) ); + + QPushButton * mdowntr_btn = new QPushButton( embed::getIconPixmap( + "arp_down_on", 12, 12 ), + "", &m_trackOperationsWidget ); + mdowntr_btn->setGeometry( 1+TRACK_OP_BTN_WIDTH, 1+TRACK_OP_BTN_HEIGHT, + TRACK_OP_BTN_WIDTH, + TRACK_OP_BTN_HEIGHT ); + connect( mdowntr_btn, SIGNAL( clicked() ), this, + SLOT( moveTrackDown() ) ); + connect( mdowntr_btn, SIGNAL( clicked() ), mdowntr_btn, + SLOT( clearFocus() ) ); + toolTip::add( mdowntr_btn, tr( "Move this track down" ) ); + + m_muteBtn = new pixmapButton( &m_trackOperationsWidget ); + m_muteBtn->setActiveGraphic( embed::getIconPixmap( "mute_on" ) ); + m_muteBtn->setInactiveGraphic( embed::getIconPixmap( "mute_off" ) ); + m_muteBtn->move( 3+TRACK_OP_BTN_WIDTH*2, 8 ); + m_muteBtn->show(); + connect( m_muteBtn, SIGNAL( toggled( bool ) ), this, + SLOT( setMuted( bool ) ) ); + connect( m_muteBtn, SIGNAL( clickedRight() ), this, + SLOT( muteBtnRightClicked() ) ); +#ifdef QT4 + m_muteBtn->setWhatsThis( +#else + QWhatsThis::add( m_muteBtn, +#endif + tr( "With this switch you can either mute this track or mute " + "all other tracks.\nBy clicking left, this track is " + "muted. This is useful, if you only want to listen to " + "the other tracks without changing this track " + "and loosing information.\nWhen you click right on " + "this switch, all other tracks will be " + "muted. This is useful, if you only want to listen to " + "this track." ) ); + toolTip::add( m_muteBtn, tr( "left click = mute this track\n" + "right click = mute all other tracks (solo)" ) ); + + +#ifdef QT4 + { + QPalette pal; + pal.setColor( m_trackSettingsWidget.backgroundRole(), + QColor( 64, 64, 64 ) ); + m_trackSettingsWidget.setPalette( pal ); + } +#else + m_trackSettingsWidget.setPaletteBackgroundColor( QColor( 64, 64, 64 ) ); +#endif + // set background-mode for flicker-free redraw +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif +} + + + + +trackWidget::~trackWidget() +{ +} + + + + +void trackWidget::paintEvent( QPaintEvent * _pe ) +{ +#ifdef QT4 + QPainter p( this ); +#else + // create pixmap for whole widget + QPixmap pm( rect().size() ); + pm.fill( QColor( 128, 128, 128 ) ); + + // and a painter for it + QPainter p( &pm ); +#endif + p.setPen( QColor( 0, 0, 0 ) ); + p.drawLine( 0, height()-1, width()-1, height()-1 ); + +#ifndef QT4 + // blit drawn pixmap to actual widget + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void trackWidget::cloneTrack( void ) +{ + m_track->getTrackContainer()->cloneTrack( m_track ); +} + + + + +void trackWidget::deleteTrack( void ) +{ + m_track->getTrackContainer()->removeTrack( m_track ); +} + + + + +void trackWidget::moveTrackUp( void ) +{ + m_track->getTrackContainer()->moveTrackUp( m_track ); +} + + + + +void trackWidget::moveTrackDown( void ) +{ + m_track->getTrackContainer()->moveTrackDown( m_track ); +} + + + + +bool trackWidget::muted( void ) const +{ +#ifdef QT4 + return( m_muteBtn->isChecked() ); +#else + return( m_muteBtn->isOn() ); +#endif +} + + + + +void trackWidget::setMuted( bool _muted ) +{ +#ifdef QT4 + m_muteBtn->setChecked( _muted ); +#else + m_muteBtn->setOn( _muted ); +#endif + m_trackContentWidget.updateTCOs(); +} + + + + +void trackWidget::muteBtnRightClicked( void ) +{ + bool m = muted(); + m_track->getTrackContainer()->setMutedOfAllTracks( m ); + setMuted( !m ); +} + + + + +midiTime trackWidget::endPosition( const midiTime & _pos_start ) +{ + const float ppt = m_track->getTrackContainer()->pixelsPerTact(); + const int cww = m_trackContentWidget.width(); + return( _pos_start + static_cast( cww * 64 / ppt ) ); +} + + + + +// resposible for moving track-content-widgets to appropriate position after +// change of visible viewport +void trackWidget::changePosition( const midiTime & _new_pos ) +{ + midiTime pos = _new_pos; + if( pos < 0 ) + { + pos = m_track->getTrackContainer()->currentPosition(); + } + + const Sint32 begin = pos; + const Sint32 end = endPosition( pos ); + const float ppt = m_track->getTrackContainer()->pixelsPerTact(); + const csize tcos = m_trackContentWidget.numOfTCOs(); + + for( csize i = 0; i < tcos; ++i ) + { + trackContentObject * tco = + m_trackContentWidget.getTCO( i ); + tco->changeLength( tco->length() ); + Sint32 ts = tco->startPosition(); + Sint32 te = tco->endPosition(); + if( ( ts >= begin && ts <= end ) || + ( te >= begin && te <= end ) || + ( ts <= begin && te >= end ) ) + { + tco->move( static_cast( ( ts - begin ) * ppt / + 64 ), tco->y() ); + tco->show(); + } + else + { + tco->hide(); + } + } +} + + + + +void trackWidget::resizeEvent( QResizeEvent * _re ) +{ + m_trackOperationsWidget.setFixedSize( TRACK_OP_WIDTH, height() - 1 ); + m_trackOperationsWidget.move( 0, 0 ); + m_trackSettingsWidget.setFixedSize( DEFAULT_SETTINGS_WIDGET_WIDTH, + height() - 1 ); + m_trackSettingsWidget.move( TRACK_OP_WIDTH, 0 ); + m_trackContentWidget.resize( m_track->getTrackContainer()->width() - + TRACK_OP_WIDTH - + DEFAULT_SETTINGS_WIDGET_WIDTH, + height() - 1 ); + m_trackContentWidget.move( m_trackOperationsWidget.width() + + m_trackSettingsWidget.width(), 0 ); +} + + + + +// =========================================================================== +// track +// =========================================================================== + +track::track( trackContainer * _tc ) : + settings(), + m_trackContainer( _tc ) +{ + m_trackWidget = new trackWidget( this, + m_trackContainer->containerWidget() ); + + m_trackContainer->addTrack( this ); +} + + + + +track::~track() +{ + m_trackContainer->removeTrack( this ); + + delete m_trackWidget; + m_trackWidget = NULL; +} + + + + +track * FASTCALL track::createTrack( trackTypes _tt, trackContainer * _tc ) +{ + switch( _tt ) + { + case CHANNEL_TRACK: return( new channelTrack( _tc ) ); + case BB_TRACK: return( new bbTrack( _tc ) ); + case SAMPLE_TRACK: return( new sampleTrack( _tc ) ); +// case EVENT_TRACK: +// case VIDEO_TRACK: + default: break; + } + qFatal( "Attempt to create track with invalid type %d!", + static_cast( _tt ) ); + return( NULL ); +} + + + +track * FASTCALL track::createTrack( const QDomElement & _this, + trackContainer * _tc ) +{ + track * t = createTrack( static_cast( _this.attribute( + "type" ).toInt() ), _tc ); +#ifdef LMMS_DEBUG + assert( t != NULL ); +#endif + t->loadSettings( _this ); + return( t ); +} + + + + +track * FASTCALL track::cloneTrack( track * _track ) +{ + QDomDocument doc; + QDomElement parent = doc.createElement( "clone" ); + _track->saveSettings( doc, parent ); + QDomElement e = parent.firstChild().toElement(); + return( createTrack( e, _track->getTrackContainer() ) ); +} + + + + +tact track::length( void ) const +{ + return( getTrackContentWidget()->length() ); +} + + + + +void FASTCALL track::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + csize num_of_tcos = getTrackContentWidget()->numOfTCOs(); + + QDomElement track_de = _doc.createElement( "track" ); + track_de.setAttribute( "type", QString::number( trackType() ) ); + track_de.setAttribute( "muted", muted() ); + _parent.appendChild( track_de ); + + // let actual track (channelTrack, bbTrack, sampleTrack etc.) save + // its settings + saveTrackSpecificSettings( _doc, track_de ); + + // now save settings of all TCO's + for( csize i = 0; i < num_of_tcos; ++i ) + { + trackContentObject * tco = getTCO( i ); + tco->saveSettings( _doc, track_de ); + } +} + + + + +void FASTCALL track::loadSettings( const QDomElement & _this ) +{ + if( _this.attribute( "type" ).toInt() != trackType() ) + { + qWarning( "Current track-type does not match track-type of " + "settings-node!\n" ); + } + + m_trackWidget->setMuted( _this.attribute( "muted" ).toInt() ); + + + getTrackContentWidget()->removeAllTCOs(); + + QDomNode node = _this.firstChild(); + while( !node.isNull() ) + { + if( node.isElement() ) + { + if( nodeName() == node.nodeName() ) + { + loadTrackSpecificSettings( node.toElement() ); + } + else + { + trackContentObject * tco = createTCO( + midiTime( 0 ) ); + tco->loadSettings( node.toElement() ); + addTCO( tco ); + } + } + node = node.nextSibling(); + } +} + + + + +trackContentObject * FASTCALL track::addTCO( trackContentObject * _tco ) +{ + return( getTrackContentWidget()->addTCO( _tco ) ); +} + + + + +void FASTCALL track::removeTCO( csize _tco_num ) +{ + getTrackContentWidget()->removeTCO( _tco_num ); +} + + + + +csize track::numOfTCOs( void ) +{ + return( getTrackContentWidget()->numOfTCOs() ); +} + + + + +trackContentObject * FASTCALL track::getTCO( csize _tco_num ) +{ + return( getTrackContentWidget()->getTCO( _tco_num ) ); + +} + + + + +csize FASTCALL track::getTCONum( trackContentObject * _tco ) +{ + for( csize i = 0; i < getTrackContentWidget()->numOfTCOs(); ++i ) + { + if( getTCO( i ) == _tco ) + { + return( i ); + } + } +#ifdef LMMS_DEBUG + qFatal( "track::getTCONum(...) -> _tco not found!\n" ); +#endif + return( 0 ); +} + + + + +void FASTCALL track::getTCOsInRange( vlist & _tco_v, + const midiTime & _start, + const midiTime & _end ) +{ + for( csize i = 0; i < getTrackContentWidget()->numOfTCOs(); ++i ) + { + trackContentObject * tco = getTCO( i ); + Sint32 s = tco->startPosition(); + Sint32 e = tco->endPosition(); + if( ( s <= _end ) && ( e >= _start ) ) + { + // ok, TCO is posated within given range + // now let's search according position for TCO in list + // -> list is ordered by TCO's position afterwards + bool inserted = FALSE; + for( vlist::iterator it = + _tco_v.begin(); + it != _tco_v.end(); ++it ) + { + if( ( *it )->startPosition() >= s ) + { + _tco_v.insert( it, tco ); + inserted = TRUE; + break; + } + } + if( inserted == FALSE ) + { + // no TCOs found posated behind current TCO... + _tco_v.push_back( tco ); + } + } + } +} + + + + +void FASTCALL track::swapPositionOfTCOs( csize _tco_num1, csize _tco_num2 ) +{ + getTrackContentWidget()->swapPositionOfTCOs( _tco_num1, _tco_num2 ); +} + + + +#include "track.moc" + diff --git a/src/core/track_container.cpp b/src/core/track_container.cpp new file mode 100644 index 000000000..24c3a815f --- /dev/null +++ b/src/core/track_container.cpp @@ -0,0 +1,391 @@ +/* + * track_container.cpp - implementation of base-class for all track-containers + * like Song-Editor, BB-Editor... + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#define setValue setProgress +#define value progress +#define maximum totalSteps + +#endif + + +#include "track_container.h" +#include "track.h" +#include "templates.h" +#include "bb_track.h" +#include "lmms_main_win.h" +#include "mixer.h" + + + +const Uint16 DEFAULT_PIXELS_PER_TACT = 16; + + +trackContainer::trackContainer() : + QMainWindow( lmmsMainWin::inst()->workspace() +#ifndef QT4 + , 0, Qt::WStyle_Title +#endif + ), + settings(), + m_currentPosition( 0, 0 ), + m_ppt( DEFAULT_PIXELS_PER_TACT ) +{ +#ifdef QT4 + lmmsMainWin::inst()->workspace()->addWindow( this ); +#endif + + m_scrollArea = new QScrollArea( this ); + m_scrollArea->setFrameStyle( QFrame::NoFrame ); + m_scrollArea->setHorizontalScrollBarPolicy( +#ifdef QT4 + Qt::ScrollBarAlwaysOff +#else + QScrollArea::AlwaysOff +#endif + ); +} + + + +trackContainer::~trackContainer() +{ + while( m_trackWidgets.size() ) + { + removeTrack( m_trackWidgets.front()->getTrack() ); + } +} + + + + +void trackContainer::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + QDomElement tc_de = _doc.createElement( "trackcontainer" ); + tc_de.setAttribute( "type", nodeName() ); + _parent.appendChild( tc_de ); + + // save settings of each track + for( trackWidgetVector::iterator it = m_trackWidgets.begin(); + it != m_trackWidgets.end(); ++it ) + { + ( *it )->getTrack()->saveSettings( _doc, tc_de ); + } +} + + + + +void trackContainer::loadSettings( const QDomElement & _this ) +{ + static QProgressDialog * pd = NULL; + bool was_null = ( pd == NULL ); + int start_val = 0; + if( pd == NULL ) + { +#ifdef QT4 + pd = new QProgressDialog( tr( "Loading project..." ), + tr( "Cancel" ), 0, + _this.childNodes().count() ); +#else + pd = new QProgressDialog( tr( "Loading project..." ), + tr( "Cancel" ), + _this.childNodes().count(), + 0, 0, TRUE ); +#endif + pd->setWindowTitle( tr( "Please wait..." ) ); + pd->show(); + } + else + { + start_val = pd->value(); +#ifdef QT4 + pd->setMaximum( pd->maximum() + _this.childNodes().count() ); +#else + pd->setTotalSteps( pd->maximum() + _this.childNodes().count() ); +#endif + } + + QDomNode node = _this.firstChild(); + while( !node.isNull() ) + { + pd->setValue( pd->value() + 1 ); +#ifdef QT4 + qApp->processEvents( QEventLoop::AllEvents, 100 ); +#else + qApp->processEvents( 100 ); +#endif + + if( pd->wasCanceled() ) + { + break; + } + + if( node.isElement() ) + { + track::createTrack( node.toElement(), this ); + } + node = node.nextSibling(); + } + + pd->setValue( start_val + _this.childNodes().count() ); + + if( was_null ) + { + delete pd; + pd = NULL; + } +} + + + + +void trackContainer::cloneTrack( track * _track ) +{ + track::cloneTrack( _track ); +} + + + + +void trackContainer::addTrack( track * _track ) +{ + m_trackWidgets.push_back( _track->getTrackWidget() ); +#ifndef QT4 + m_scrollArea->addChild( _track->getTrackWidget() ); +#endif + connect( this, SIGNAL( positionChanged( const midiTime & ) ), + _track->getTrackWidget(), + SLOT( changePosition( const midiTime & ) ) ); + realignTracks(); +} + + + + +void trackContainer::removeTrack( track * _track ) +{ + trackWidgetVector::iterator it = qFind( m_trackWidgets.begin(), + m_trackWidgets.end(), _track->getTrackWidget() ); + if( it != m_trackWidgets.end() ) + { + mixer::inst()->pause(); +#ifndef QT4 + m_scrollArea->removeChild( _track->getTrackWidget() ); +#endif + m_trackWidgets.erase( it ); + + delete _track; + + mixer::inst()->play(); + + realignTracks(); + } +} + + + + +void trackContainer::moveTrackUp( track * _track ) +{ + for( trackWidgetVector::iterator it = m_trackWidgets.begin(); + it != m_trackWidgets.end(); ++it ) + { + if( *it == _track->getTrackWidget() && + it > m_trackWidgets.begin() ) + { + bbTrack::swapBBTracks( ( *it )->getTrack(), + ( *( it - 1 ) )->getTrack() ); + qSwap( *it, *( it - 1 ) ); + realignTracks(); + break; + } + } +} + + + + +void trackContainer::moveTrackDown( track * _track ) +{ + for( trackWidgetVector::iterator it = m_trackWidgets.begin(); + it != m_trackWidgets.end(); ++it ) + { + if( *it == _track->getTrackWidget() && + it + 1 < m_trackWidgets.end() ) + { + bbTrack::swapBBTracks( ( *it )->getTrack(), + ( *( it + 1 ) )->getTrack() ); + qSwap( *it, *( it + 1 ) ); + realignTracks(); + break; + } + } +} + + + + +void trackContainer::updateAfterTrackAdd( void ) +{ +} + + + + +void trackContainer::realignTracks( bool _complete_update ) +{ + int y = 0; + for( trackWidgetVector::iterator it = m_trackWidgets.begin(); + it != m_trackWidgets.end(); ++it ) + { + if( _complete_update ) + { + ( *it )->hide(); + } + ( *it )->show(); +#ifdef QT4 + ( *it )->move( 0, y ); +#else + m_scrollArea->moveChild( *it, 0, y ); +#endif + ( *it )->resize( width(), ( *it )->height() ); + ( *it )->changePosition( m_currentPosition ); + y += ( *it )->height(); + } +#ifndef QT4 + m_scrollArea->resizeContents( m_scrollArea->parentWidget()->width(), + y ); +#endif + updateScrollArea(); +} + + + + +unsigned int trackContainer::countTracks( track::trackTypes _tt ) const +{ + unsigned int cnt = 0; + for( trackWidgetVector::const_iterator it = m_trackWidgets.begin(); + it != m_trackWidgets.end(); ++it ) + { + if( ( *it )->getTrack()->trackType() == _tt || + _tt == track::TOTAL_TRACK_TYPES ) + { + ++cnt; + } + } + return( cnt ); +} + + + + +void trackContainer::setMutedOfAllTracks( bool _muted ) +{ + for( trackWidgetVector::iterator it = m_trackWidgets.begin(); + it != m_trackWidgets.end(); ++it ) + { + ( *it )->setMuted( _muted ); + } +} + + + + +constTrackVector trackContainer::tracks( void ) const +{ + constTrackVector tracks; + for( trackWidgetVector::const_iterator it = m_trackWidgets.begin(); + it != m_trackWidgets.end(); ++it ) + { + tracks.push_back( ( *it )->getTrack() ); + } + return( tracks ); +} + + + + +trackVector trackContainer::tracks( void ) +{ + trackVector tracks; + for( trackWidgetVector::iterator it = m_trackWidgets.begin(); + it != m_trackWidgets.end(); ++it ) + { + tracks.push_back( ( *it )->getTrack() ); + } + return( tracks ); +} + + + + +void trackContainer::setPixelsPerTact( Uint16 _ppt ) +{ + m_ppt = _ppt; +} + + + + +void trackContainer::resizeEvent( QResizeEvent * ) +{ + realignTracks(); +} + + + + +void trackContainer::updateScrollArea( void ) +{ + m_scrollArea->resize( tMax( m_scrollArea->parentWidget()->width() - + m_scrollArea->x()-2, 0 ), + tMax( m_scrollArea->parentWidget()->height() - + m_scrollArea->y() - 2, 0 ) ); + //m_scrollArea->updateContents(); +} + + + + +#include "track_container.moc" + +#undef setValue +#undef value +#undef maximum