1 /*
2  * GigPlayer.h - a GIG player using libgig (based on Sf2 player plugin)
3  *
4  * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
5  * Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
6  *
7  * This file is part of LMMS - https://lmms.io
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public
20  * License along with this program (see COPYING); if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301 USA.
23  *
24  */
25 
26 
27 #ifndef GIG_PLAYER_H
28 #define GIG_PLAYER_H
29 
30 #include <QList>
31 #include <QMutex>
32 #include <QMutexLocker>
33 #include <samplerate.h>
34 
35 #include "Instrument.h"
36 #include "PixmapButton.h"
37 #include "InstrumentView.h"
38 #include "Knob.h"
39 #include "LcdSpinBox.h"
40 #include "LedCheckbox.h"
41 #include "MemoryManager.h"
42 #include "gig.h"
43 
44 class GigInstrumentView;
45 class NotePlayHandle;
46 
47 class PatchesDialog;
48 class QLabel;
49 
50 
51 
52 
53 struct GIGPluginData
54 {
55 	int midiNote;
56 } ;
57 
58 
59 
60 
61 // Load a GIG file using libgig
62 class GigInstance
63 {
64 public:
GigInstance(QString filename)65 	GigInstance( QString filename ) :
66 		riff( filename.toUtf8().constData() ),
67 		gig( &riff )
68 	{}
69 
70 private:
71 	RIFF::File riff;
72 
73 public:
74 	gig::File gig;
75 } ;
76 
77 
78 
79 
80 // Stores options for the notes, e.g. velocity and release time
81 struct Dimension
82 {
DimensionDimension83 	Dimension() :
84 		release( false )
85 	{
86 		for( int i = 0; i < 8; ++i )
87 		{
88 			DimValues[i] = 0;
89 		}
90 	}
91 
92 	uint DimValues[8];
93 	bool release;
94 } ;
95 
96 
97 
98 
99 // Takes information from the GIG file for a certain note and provides the
100 // amplitude (0-1) to multiply the signal by (internally incrementing the
101 // position in the envelope when asking for the amplitude).
102 class ADSR
103 {
104 	// From the file
105 	float preattack; // initial amplitude (0-1)
106 	float attack; // 0-60s
107 	float decay1; // 0-60s
108 	float decay2; // 0-60s
109 	bool infiniteSustain; // i.e., no decay2
110 	float sustain; // sustain amplitude (0-1)
111 	float release; // 0-60s
112 
113 	// Used to calculate current amplitude
114 	float amplitude;
115 	bool isAttack;
116 	bool isRelease;
117 	bool isDone;
118 	f_cnt_t attackPosition;
119 	f_cnt_t attackLength;
120 	f_cnt_t decayLength;
121 	f_cnt_t releasePosition;
122 	f_cnt_t releaseLength;
123 
124 public:
125 	ADSR();
126 	ADSR( gig::DimensionRegion * region, int sampleRate );
127 	void keyup(); // We will begin releasing starting now
128 	bool done(); // Is this sample done playing?
129 	float value(); // What's the current amplitude
130 	void inc( f_cnt_t num ); // Increment internal positions by num
131 } ;
132 
133 
134 
135 
136 // The sample from the GIG file with our current position in both the sample
137 // and the envelope
138 class GigSample
139 {
140 public:
141 	GigSample( gig::Sample * pSample, gig::DimensionRegion * pDimRegion,
142 			float attenuation, int interpolation, float desiredFreq );
143 	~GigSample();
144 
145 	// Needed when initially creating in QList
146 	GigSample( const GigSample& g );
147 	GigSample& operator=( const GigSample& g );
148 
149 	// Needed since libsamplerate stores data internally between calls
150 	void updateSampleRate();
151 	bool convertSampleRate( sampleFrame & oldBuf, sampleFrame & newBuf,
152 		f_cnt_t oldSize, f_cnt_t newSize, float freq_factor, f_cnt_t& used );
153 
154 	gig::Sample * sample;
155 	gig::DimensionRegion * region;
156 	float attenuation;
157 	ADSR adsr;
158 
159 	// The position in sample
160 	f_cnt_t pos;
161 
162 	// Whether to change the pitch of the samples, e.g. if there's only one
163 	// sample per octave and you want that sample pitch shifted for the rest of
164 	// the notes in the octave, this will be true
165 	bool pitchtrack;
166 
167 	// Used to convert sample rates
168 	int interpolation;
169 	SRC_STATE * srcState;
170 
171 	// Used changing the pitch of the note if desired
172 	float sampleFreq;
173 	float freqFactor;
174 } ;
175 
176 
177 
178 
179 // What portion of a note are we in?
180 enum GigState
181 {
182 	// We just pressed the key
183 	KeyDown,
184 	// The note is currently playing
185 	PlayingKeyDown,
186 	// We just released the key
187 	KeyUp,
188 	// The note is being released, e.g. a release sample is playing
189 	PlayingKeyUp,
190 	// The note is done playing, you can delete this note now
191 	Completed
192 } ;
193 
194 
195 
196 
197 // Corresponds to a certain midi note pressed, but may contain multiple samples
198 class GigNote
199 {
200 public:
201 	int midiNote;
202 	int velocity;
203 	bool release; // Whether to trigger a release sample on key up
204 	bool isRelease; // Whether this is a release sample, changes when we delete it
205 	GigState state;
206 	float frequency;
207 	QList<GigSample> samples;
208 
209 	// Used to determine which note should be released on key up
210 	//
211 	// Note: if accessing the data, be careful not to access it after the key
212 	// has been released since that's when it is deleted
213 	GIGPluginData * handle;
214 
GigNote(int midiNote,int velocity,float frequency,GIGPluginData * handle)215 	GigNote( int midiNote, int velocity, float frequency, GIGPluginData * handle )
216 		: midiNote( midiNote ), velocity( velocity ),
217 		  release( false ), isRelease( false ), state( KeyDown ),
218 		  frequency( frequency ), handle( handle )
219 	{
220 	}
221 } ;
222 
223 
224 
225 
226 class GigInstrument : public Instrument
227 {
228 	Q_OBJECT
229 	MM_OPERATORS
230 
231 	mapPropertyFromModel( int, getBank, setBank, m_bankNum );
232 	mapPropertyFromModel( int, getPatch, setPatch, m_patchNum );
233 
234 public:
235 	GigInstrument( InstrumentTrack * _instrument_track );
236 	virtual ~GigInstrument();
237 
238 	virtual void play( sampleFrame * _working_buffer );
239 
240 	virtual void playNote( NotePlayHandle * _n,
241 						sampleFrame * _working_buffer );
242 	virtual void deleteNotePluginData( NotePlayHandle * _n );
243 
244 
245 	virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
246 	virtual void loadSettings( const QDomElement & _this );
247 
248 	virtual void loadFile( const QString & _file );
249 
250 	virtual AutomatableModel * childModel( const QString & _modelName );
251 
252 	virtual QString nodeName() const;
253 
desiredReleaseFrames()254 	virtual f_cnt_t desiredReleaseFrames() const
255 	{
256 		return 0;
257 	}
258 
flags()259 	virtual Flags flags() const
260 	{
261 		return IsSingleStreamed|IsNotBendable;
262 	}
263 
264 	virtual PluginView * instantiateView( QWidget * _parent );
265 
266 	QString getCurrentPatchName();
267 
268 
269 	void setParameter( const QString & _param, const QString & _value );
270 
271 
272 public slots:
273 	void openFile( const QString & _gigFile, bool updateTrackName = true );
274 	void updatePatch();
275 	void updateSampleRate();
276 
277 
278 private:
279 	// The GIG file and instrument we're using
280 	GigInstance * m_instance;
281 	gig::Instrument * m_instrument;
282 
283 	// Part of the UI
284 	QString m_filename;
285 
286 	LcdSpinBoxModel m_bankNum;
287 	LcdSpinBoxModel m_patchNum;
288 
289 	FloatModel m_gain;
290 
291 	// Locking for the data
292 	QMutex m_synthMutex;
293 	QMutex m_notesMutex;
294 
295 	// Used for resampling
296 	int m_interpolation;
297 
298 	// List of all the currently playing notes
299 	QList<GigNote> m_notes;
300 
301 	// Used when determining which samples to use
302 	uint32_t m_RandomSeed;
303 	float m_currentKeyDimension;
304 
305 private:
306 	// Delete the current GIG instance if one is open
307 	void freeInstance();
308 
309 	// Open the instrument in the currently-open GIG file
310 	void getInstrument();
311 
312 	// Create "dimension" to select desired samples from GIG file based on
313 	// parameters such as velocity
314 	Dimension getDimensions( gig::Region * pRegion, int velocity, bool release );
315 
316 	// Load sample data from the Gig file, looping the sample where needed
317 	void loadSample( GigSample& sample, sampleFrame* sampleData, f_cnt_t samples );
318 	f_cnt_t getLoopedIndex( f_cnt_t index, f_cnt_t startf, f_cnt_t endf ) const;
319 	f_cnt_t getPingPongIndex( f_cnt_t index, f_cnt_t startf, f_cnt_t endf ) const;
320 
321 	// Add the desired samples to the note, either normal samples or release
322 	// samples
323 	void addSamples( GigNote & gignote, bool wantReleaseSample );
324 
325 	friend class GigInstrumentView;
326 
327 signals:
328 	void fileLoading();
329 	void fileChanged();
330 	void patchChanged();
331 
332 } ;
333 
334 
335 
336 
337 class GigInstrumentView : public InstrumentView
338 {
339 	Q_OBJECT
340 public:
341 	GigInstrumentView( Instrument * _instrument,
342 					QWidget * _parent );
343 	virtual ~GigInstrumentView();
344 
345 private:
346 	virtual void modelChanged();
347 
348 	PixmapButton * m_fileDialogButton;
349 	PixmapButton * m_patchDialogButton;
350 
351 	LcdSpinBox * m_bankNumLcd;
352 	LcdSpinBox * m_patchNumLcd;
353 
354 	QLabel * m_filenameLabel;
355 	QLabel * m_patchLabel;
356 
357 	Knob * m_gainKnob;
358 
359 	static PatchesDialog * s_patchDialog;
360 
361 protected slots:
362 	void invalidateFile();
363 	void showFileDialog();
364 	void showPatchDialog();
365 	void updateFilename();
366 	void updatePatchName();
367 } ;
368 
369 
370 #endif
371