1 /*
2  * LocalZynAddSubFx.cpp - local implementation of ZynAddSubFx plugin
3  *
4  * Copyright (c) 2009-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 #include <lmmsconfig.h>
26 
27 #include "zynaddsubfx/src/Misc/Util.h"
28 #include <unistd.h>
29 #include <ctime>
30 
31 #include "LocalZynAddSubFx.h"
32 
33 #include "zynaddsubfx/src/Nio/NulEngine.h"
34 #include "zynaddsubfx/src/Misc/Master.h"
35 #include "zynaddsubfx/src/Misc/Part.h"
36 #include "zynaddsubfx/src/Misc/Dump.h"
37 
38 
39 SYNTH_T* synth = NULL;
40 
41 int LocalZynAddSubFx::s_instanceCount = 0;
42 
43 
LocalZynAddSubFx()44 LocalZynAddSubFx::LocalZynAddSubFx() :
45 	m_master( NULL ),
46 	m_ioEngine( NULL )
47 {
48 	for( int i = 0; i < NumKeys; ++i )
49 	{
50 		m_runningNotes[i] = 0;
51 	}
52 
53 	if( s_instanceCount == 0 )
54 	{
55 #ifdef LMMS_BUILD_WIN32
56 #ifndef __WINPTHREADS_VERSION
57 		// (non-portable) initialization of statically linked pthread library
58 		pthread_win32_process_attach_np();
59 		pthread_win32_thread_attach_np();
60 #endif
61 #endif
62 
63 		initConfig();
64 
65 		synth = new SYNTH_T;
66 		synth->oscilsize = config.cfg.OscilSize;
67 		synth->alias();
68 
69 		srand( time( NULL ) );
70 
71 		denormalkillbuf = new float[synth->buffersize];
72 		for( int i = 0; i < synth->buffersize; ++i )
73 		{
74 			denormalkillbuf[i] = (RND-0.5)*1e-16;
75 		}
76 	}
77 
78 	++s_instanceCount;
79 
80 	m_ioEngine = new NulEngine;
81 
82 	m_master = new Master();
83 	m_master->swaplr = 0;
84 }
85 
86 
87 
88 
~LocalZynAddSubFx()89 LocalZynAddSubFx::~LocalZynAddSubFx()
90 {
91 	delete m_master;
92 	delete m_ioEngine;
93 
94 	if( --s_instanceCount == 0 )
95 	{
96 		delete[] denormalkillbuf;
97 	}
98 }
99 
100 
101 
102 
initConfig()103 void LocalZynAddSubFx::initConfig()
104 {
105 	config.init();
106 
107 	config.cfg.GzipCompression = 0;
108 }
109 
110 
111 
112 
setSampleRate(int sampleRate)113 void LocalZynAddSubFx::setSampleRate( int sampleRate )
114 {
115 	synth->samplerate = sampleRate;
116 	synth->alias();
117 }
118 
119 
120 
121 
setBufferSize(int bufferSize)122 void LocalZynAddSubFx::setBufferSize( int bufferSize )
123 {
124 	synth->buffersize = bufferSize;
125 	synth->alias();
126 }
127 
128 
129 
130 
saveXML(const std::string & _filename)131 void LocalZynAddSubFx::saveXML( const std::string & _filename )
132 {
133 	char * name = strdup( _filename.c_str() );
134 	m_master->saveXML( name );
135 	free( name );
136 }
137 
138 
139 
140 
loadXML(const std::string & _filename)141 void LocalZynAddSubFx::loadXML( const std::string & _filename )
142 {
143 	char * f = strdup( _filename.c_str() );
144 
145 	pthread_mutex_lock( &m_master->mutex );
146 	m_master->defaults();
147 	m_master->loadXML( f );
148 	pthread_mutex_unlock( &m_master->mutex );
149 
150 	m_master->applyparameters();
151 
152 	unlink( f );
153 	free( f );
154 }
155 
156 
157 
158 
loadPreset(const std::string & _filename,int _part)159 void LocalZynAddSubFx::loadPreset( const std::string & _filename, int _part )
160 {
161 	char * f = strdup( _filename.c_str() );
162 
163 	pthread_mutex_lock( &m_master->mutex );
164 	m_master->part[_part]->defaultsinstrument();
165 	m_master->part[_part]->loadXMLinstrument( f );
166 	pthread_mutex_unlock( &m_master->mutex );
167 
168 	m_master->applyparameters();
169 
170 	free( f );
171 }
172 
173 
174 
175 
setPresetDir(const std::string & _dir)176 void LocalZynAddSubFx::setPresetDir( const std::string & _dir )
177 {
178 	m_presetsDir = _dir;
179 	for( int i = 0; i < MAX_BANK_ROOT_DIRS; ++i )
180 	{
181 		if( config.cfg.bankRootDirList[i].empty() )
182 		{
183 			config.cfg.bankRootDirList[i] = m_presetsDir;
184 			break;
185 		}
186 		else if( config.cfg.bankRootDirList[i] == m_presetsDir )
187 		{
188 			break;
189 		}
190 	}
191 }
192 
193 
194 
195 
setLmmsWorkingDir(const std::string & _dir)196 void LocalZynAddSubFx::setLmmsWorkingDir( const std::string & _dir )
197 {
198 	if( config.workingDir != NULL )
199 	{
200 		free( config.workingDir );
201 	}
202 	config.workingDir = strdup( _dir.c_str() );
203 
204 	initConfig();
205 }
206 
207 
208 
setPitchWheelBendRange(int semitones)209 void LocalZynAddSubFx::setPitchWheelBendRange( int semitones )
210 {
211 	for( int i = 0; i < NUM_MIDI_PARTS; ++i )
212 	{
213 		m_master->part[i]->ctl.setpitchwheelbendrange( semitones * 100 );
214 	}
215 }
216 
217 
218 
processMidiEvent(const MidiEvent & event)219 void LocalZynAddSubFx::processMidiEvent( const MidiEvent& event )
220 {
221 	switch( event.type() )
222 	{
223 		case MidiNoteOn:
224 			if( event.velocity() > 0 )
225 			{
226 				if( event.key() < 0 || event.key() > MidiMaxKey )
227 				{
228 					break;
229 				}
230 				if( m_runningNotes[event.key()] > 0 )
231 				{
232 					m_master->noteOff( event.channel(), event.key() );
233 				}
234 				++m_runningNotes[event.key()];
235 				m_master->noteOn( event.channel(), event.key(), event.velocity() );
236 				break;
237 			}
238 		case MidiNoteOff:
239 			if( event.key() < 0 || event.key() > MidiMaxKey )
240 			{
241 				break;
242 			}
243 			if( --m_runningNotes[event.key()] <= 0 )
244 			{
245 				m_master->noteOff( event.channel(), event.key() );
246 			}
247 			break;
248 		case MidiPitchBend:
249 			m_master->setController( event.channel(), C_pitchwheel, event.pitchBend()-8192 );
250 			break;
251 		case MidiControlChange:
252 			m_master->setController( event.channel(), event.controllerNumber(), event.controllerValue() );
253 			break;
254 		default:
255 			break;
256 	}
257 }
258 
259 
260 
261 
processAudio(sampleFrame * _out)262 void LocalZynAddSubFx::processAudio( sampleFrame * _out )
263 {
264 	float outputl[synth->buffersize];
265 	float outputr[synth->buffersize];
266 
267 	m_master->GetAudioOutSamples( synth->buffersize, synth->samplerate, outputl, outputr );
268 
269 	// TODO: move to MixHelpers
270 	for( int f = 0; f < synth->buffersize; ++f )
271 	{
272 		_out[f][0] = outputl[f];
273 		_out[f][1] = outputr[f];
274 	}
275 }
276 
277 
278