1 /*
2  * Note.cpp - implementation of class note
3  *
4  * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5  *
6  * This file is part of LMMS - https://lmms.io
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public
19  * License along with this program (see COPYING); if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301 USA.
22  *
23  */
24 
25 
26 #include <QDomElement>
27 
28 #include <math.h>
29 
30 #include "Note.h"
31 #include "DetuningHelper.h"
32 
33 
Note(const MidiTime & length,const MidiTime & pos,int key,volume_t volume,panning_t panning,DetuningHelper * detuning)34 Note::Note( const MidiTime & length, const MidiTime & pos,
35 		int key, volume_t volume, panning_t panning,
36 						DetuningHelper * detuning ) :
37 	m_selected( false ),
38 	m_oldKey( qBound( 0, key, NumKeys ) ),
39 	m_oldPos( pos ),
40 	m_oldLength( length ),
41 	m_isPlaying( false ),
42 	m_key( qBound( 0, key, NumKeys ) ),
43 	m_volume( qBound( MinVolume, volume, MaxVolume ) ),
44 	m_panning( qBound( PanningLeft, panning, PanningRight ) ),
45 	m_length( length ),
46 	m_pos( pos ),
47 	m_detuning( NULL )
48 {
49 	if( detuning )
50 	{
51 		m_detuning = sharedObject::ref( detuning );
52 	}
53 	else
54 	{
55 		createDetuning();
56 	}
57 }
58 
59 
60 
61 
Note(const Note & note)62 Note::Note( const Note & note ) :
63 	SerializingObject( note ),
64 	m_selected( note.m_selected ),
65 	m_oldKey( note.m_oldKey ),
66 	m_oldPos( note.m_oldPos ),
67 	m_oldLength( note.m_oldLength ),
68 	m_isPlaying( note.m_isPlaying ),
69 	m_key( note.m_key),
70 	m_volume( note.m_volume ),
71 	m_panning( note.m_panning ),
72 	m_length( note.m_length ),
73 	m_pos( note.m_pos ),
74 	m_detuning( NULL )
75 {
76 	if( note.m_detuning )
77 	{
78 		m_detuning = sharedObject::ref( note.m_detuning );
79 	}
80 }
81 
82 
83 
84 
~Note()85 Note::~Note()
86 {
87 	if( m_detuning )
88 	{
89 		sharedObject::unref( m_detuning );
90 	}
91 }
92 
93 
94 
95 
setLength(const MidiTime & length)96 void Note::setLength( const MidiTime & length )
97 {
98 	m_length = length;
99 }
100 
101 
102 
103 
setPos(const MidiTime & pos)104 void Note::setPos( const MidiTime & pos )
105 {
106 	m_pos = pos;
107 }
108 
109 
110 
111 
setKey(const int key)112 void Note::setKey( const int key )
113 {
114 	const int k = qBound( 0, key, NumKeys - 1 );
115 	m_key = k;
116 }
117 
118 
119 
120 
setVolume(volume_t volume)121 void Note::setVolume( volume_t volume )
122 {
123 	const volume_t v = qBound( MinVolume, volume, MaxVolume );
124 	m_volume = v;
125 }
126 
127 
128 
129 
setPanning(panning_t panning)130 void Note::setPanning( panning_t panning )
131 {
132 	const panning_t p = qBound( PanningLeft, panning, PanningRight );
133 	m_panning = p;
134 }
135 
136 
137 
138 
quantized(const MidiTime & m,const int qGrid)139 MidiTime Note::quantized( const MidiTime & m, const int qGrid )
140 {
141 	float p = ( (float) m / qGrid );
142 	if( p - floorf( p ) < 0.5f )
143 	{
144 		return static_cast<int>( p ) * qGrid;
145 	}
146 	return static_cast<int>( p + 1 ) * qGrid;
147 }
148 
149 
150 
151 
quantizeLength(const int qGrid)152 void Note::quantizeLength( const int qGrid )
153 {
154 	setLength( quantized( length(), qGrid ) );
155 	if( length() == 0 )
156 	{
157 		setLength( qGrid );
158 	}
159 }
160 
161 
162 
163 
quantizePos(const int qGrid)164 void Note::quantizePos( const int qGrid )
165 {
166 	setPos( quantized( pos(), qGrid ) );
167 }
168 
169 
170 
171 
saveSettings(QDomDocument & doc,QDomElement & parent)172 void Note::saveSettings( QDomDocument & doc, QDomElement & parent )
173 {
174 	parent.setAttribute( "key", m_key );
175 	parent.setAttribute( "vol", m_volume );
176 	parent.setAttribute( "pan", m_panning );
177 	parent.setAttribute( "len", m_length );
178 	parent.setAttribute( "pos", m_pos );
179 
180 	if( m_detuning && m_length )
181 	{
182 		m_detuning->saveSettings( doc, parent );
183 	}
184 }
185 
186 
187 
188 
loadSettings(const QDomElement & _this)189 void Note::loadSettings( const QDomElement & _this )
190 {
191 	const int oldKey = _this.attribute( "tone" ).toInt() + _this.attribute( "oct" ).toInt() * KeysPerOctave;
192 	m_key = qMax( oldKey, _this.attribute( "key" ).toInt() );
193 	m_volume = _this.attribute( "vol" ).toInt();
194 	m_panning = _this.attribute( "pan" ).toInt();
195 	m_length = _this.attribute( "len" ).toInt();
196 	m_pos = _this.attribute( "pos" ).toInt();
197 
198 	if( _this.hasChildNodes() )
199 	{
200 		createDetuning();
201 		m_detuning->loadSettings( _this );
202 	}
203 }
204 
205 
206 
207 
208 
createDetuning()209 void Note::createDetuning()
210 {
211 	if( m_detuning == NULL )
212 	{
213 		m_detuning = new DetuningHelper;
214 		(void) m_detuning->automationPattern();
215 		m_detuning->setRange( -MaxDetuning, MaxDetuning, 0.5f );
216 		m_detuning->automationPattern()->setProgressionType( AutomationPattern::LinearProgression );
217 	}
218 }
219 
220 
221 
222 
hasDetuningInfo() const223 bool Note::hasDetuningInfo() const
224 {
225 	return m_detuning && m_detuning->hasAutomation();
226 }
227 
228 
229 
withinRange(int tickStart,int tickEnd) const230 bool Note::withinRange(int tickStart, int tickEnd) const
231 {
232 	return pos().getTicks() >= tickStart && pos().getTicks() <= tickEnd
233 		&& length().getTicks() != 0;
234 }
235