mirror of
https://github.com/LMMS/lmms.git
synced 2026-03-13 19:52:12 -04:00
git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@124 0778d3d1-df1d-0410-868b-ea421aaaa00d
395 lines
9.4 KiB
C++
395 lines
9.4 KiB
C++
#ifndef SINGLE_SOURCE_COMPILE
|
|
|
|
/*
|
|
* note_play_handle.cpp - implementation of class notePlayHandle, part of
|
|
* play-engine
|
|
*
|
|
* Copyright (c) 2004-2006 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
|
*
|
|
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* 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 "note_play_handle.h"
|
|
#include "instrument_track.h"
|
|
#include "envelope_tab_widget.h"
|
|
#include "midi.h"
|
|
#include "midi_port.h"
|
|
#include "song_editor.h"
|
|
#include "piano_widget.h"
|
|
#include "config_mgr.h"
|
|
#include "project_journal.h"
|
|
|
|
|
|
notePlayHandle::notePlayHandle( instrumentTrack * _it,
|
|
const f_cnt_t _frames_ahead,
|
|
const f_cnt_t _frames,
|
|
const note & _n,
|
|
const bool _arp_note ) :
|
|
playHandle( NOTE_PLAY_HANDLE ),
|
|
note( NULL, _n.length(), _n.pos(), _n.tone(), _n.octave(),
|
|
_n.getVolume(), _n.getPanning() ),
|
|
m_pluginData( NULL ),
|
|
m_filter( NULL ),
|
|
m_instrumentTrack( _it ),
|
|
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 );
|
|
if( !configManager::inst()->value( "ui",
|
|
"manualchannelpiano" ).toInt() )
|
|
{
|
|
m_instrumentTrack->m_pianoWidget->setKeyState( key(), TRUE );
|
|
}
|
|
// send MIDI-note-on-event
|
|
m_instrumentTrack->processOutEvent( midiEvent( NOTE_ON,
|
|
m_instrumentTrack->m_midiPort->outputChannel(),
|
|
key(),
|
|
tLimit<Uint16>(
|
|
(Uint16) ( ( getVolume() / 100.0f ) *
|
|
( m_instrumentTrack->getVolume() / 100.0f ) *
|
|
127 ), 0, 127 ) ),
|
|
midiTime::fromFrames( m_framesAhead,
|
|
m_instrumentTrack->eng()->getSongEditor()->framesPerTact() ) );
|
|
}
|
|
|
|
|
|
|
|
|
|
notePlayHandle::~notePlayHandle()
|
|
{
|
|
if( m_released == FALSE )
|
|
{
|
|
noteOff( 0 );
|
|
}
|
|
|
|
if( m_instrumentTrack != NULL )
|
|
{
|
|
m_instrumentTrack->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_instrumentTrack == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( m_released == FALSE &&
|
|
m_totalFramesPlayed +
|
|
m_instrumentTrack->eng()->getMixer()->framesPerAudioBuffer() >=
|
|
m_frames )
|
|
{
|
|
noteOff( m_frames - m_totalFramesPlayed );
|
|
}
|
|
|
|
// play note!
|
|
m_instrumentTrack->playNote( this );
|
|
|
|
if( m_released == TRUE )
|
|
{
|
|
f_cnt_t todo =
|
|
m_instrumentTrack->eng()->getMixer()->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 *
|
|
m_instrumentTrack->eng()->getMixer()->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 <=
|
|
m_instrumentTrack->eng()->getMixer()->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 -=
|
|
m_instrumentTrack->eng()->getMixer()->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 +=
|
|
m_instrumentTrack->eng()->getMixer()->framesPerAudioBuffer();
|
|
}
|
|
|
|
|
|
|
|
|
|
void notePlayHandle::checkValidity( void )
|
|
{
|
|
if( m_instrumentTrack != NULL &&
|
|
m_instrumentTrack->type() == track::NULL_TRACK )
|
|
{
|
|
// track-type being track::NULL_TRACK indicates a track whose
|
|
// removal is in progress, so we have to invalidate ourself
|
|
if( m_released == FALSE )
|
|
{
|
|
noteOff( 0 );
|
|
}
|
|
m_instrumentTrack->deleteNotePluginData( this );
|
|
m_instrumentTrack = NULL;
|
|
}
|
|
// sub-notes might not be registered at mixer (for example arpeggio-
|
|
// notes), so they wouldn't invalidate them-selves
|
|
for( notePlayHandleVector::iterator it = m_subNotes.begin();
|
|
it != m_subNotes.end(); ++it )
|
|
{
|
|
( *it )->checkValidity();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void notePlayHandle::noteOff( const f_cnt_t _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_instrumentTrack != NULL )
|
|
{
|
|
m_releaseFramesToDo = tMax<f_cnt_t>( 10,
|
|
m_instrumentTrack->m_envWidget->releaseFrames() );
|
|
if( !configManager::inst()->value( "ui",
|
|
"manualchannelpiano" ).toInt() )
|
|
{
|
|
m_instrumentTrack->m_pianoWidget->setKeyState( key(),
|
|
FALSE );
|
|
}
|
|
// send MIDI-note-off-event
|
|
m_instrumentTrack->processOutEvent( midiEvent( NOTE_OFF,
|
|
m_instrumentTrack->m_midiPort->outputChannel(),
|
|
key(), 0 ),
|
|
midiTime::fromFrames(
|
|
m_framesBeforeRelease,
|
|
m_instrumentTrack->eng()->getSongEditor()->framesPerTact() ) );
|
|
}
|
|
else
|
|
{
|
|
m_releaseFramesToDo = 10;
|
|
}
|
|
|
|
m_released = TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
f_cnt_t notePlayHandle::actualReleaseFramesToDo( void ) const
|
|
{
|
|
return( ( m_instrumentTrack != NULL ) ?
|
|
m_instrumentTrack->m_envWidget->releaseFrames(
|
|
arpBaseNote() ) : 0 );
|
|
}
|
|
|
|
|
|
|
|
|
|
void notePlayHandle::setFrames( const f_cnt_t _frames )
|
|
{
|
|
m_frames = _frames;
|
|
if( m_frames == 0 && m_instrumentTrack != NULL )
|
|
{
|
|
m_frames = m_instrumentTrack->beatLen( this );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
float notePlayHandle::volumeLevel( const f_cnt_t _frame )
|
|
{
|
|
return( ( m_instrumentTrack != NULL ) ?
|
|
m_instrumentTrack->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;
|
|
}
|
|
|
|
|
|
|
|
|
|
int notePlayHandle::index( void ) const
|
|
{
|
|
const playHandleVector & phv =
|
|
m_instrumentTrack->eng()->getMixer()->playHandles();
|
|
int idx = 0;
|
|
for( constPlayHandleVector::const_iterator it = phv.begin();
|
|
it != phv.end(); ++it )
|
|
{
|
|
const notePlayHandle * nph =
|
|
dynamic_cast<const notePlayHandle *>( *it );
|
|
if( nph == NULL ||
|
|
nph->m_instrumentTrack != m_instrumentTrack ||
|
|
nph->released() == TRUE )
|
|
{
|
|
continue;
|
|
}
|
|
if( nph == this )
|
|
{
|
|
break;
|
|
}
|
|
++idx;
|
|
}
|
|
return( idx );
|
|
}
|
|
|
|
|
|
|
|
|
|
constNotePlayHandleVector notePlayHandle::nphsOfInstrumentTrack(
|
|
const instrumentTrack * _it )
|
|
{
|
|
const playHandleVector & phv = _it->eng()->getMixer()->playHandles();
|
|
constNotePlayHandleVector cnphv;
|
|
|
|
for( constPlayHandleVector::const_iterator it = phv.begin();
|
|
it != phv.end(); ++it )
|
|
{
|
|
const notePlayHandle * nph =
|
|
dynamic_cast<const notePlayHandle *>( *it );
|
|
if( nph != NULL && nph->m_instrumentTrack == _it &&
|
|
nph->released() == FALSE )
|
|
{
|
|
cnphv.push_back( nph );
|
|
}
|
|
}
|
|
return( cnphv );
|
|
}
|
|
|
|
|
|
|
|
bool notePlayHandle::operator==( const notePlayHandle & _nph ) const
|
|
{
|
|
return( length() == _nph.length() &&
|
|
pos() == _nph.pos() &&
|
|
key() == _nph.key() &&
|
|
getVolume() == _nph.getVolume() &&
|
|
getPanning() == _nph.getPanning() &&
|
|
m_instrumentTrack == _nph.m_instrumentTrack &&
|
|
m_frames == _nph.m_frames &&
|
|
m_framesAhead == _nph.m_framesAhead &&
|
|
m_totalFramesPlayed == _nph.m_totalFramesPlayed &&
|
|
m_released == _nph.m_released &&
|
|
m_baseNote == _nph.m_baseNote &&
|
|
m_arpNote == _nph.m_arpNote &&
|
|
m_muted == _nph.m_muted );
|
|
}
|
|
|
|
|
|
|
|
#endif
|