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