1 |
/*************************************************************************** |
2 |
* * |
3 |
* LinuxSampler - modular, streaming capable sampler * |
4 |
* * |
5 |
* Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * |
6 |
* Copyright (C) 2005 - 2012 Christian Schoenebeck * |
7 |
* * |
8 |
* This program is free software; you can redistribute it and/or modify * |
9 |
* it under the terms of the GNU General Public License as published by * |
10 |
* the Free Software Foundation; either version 2 of the License, or * |
11 |
* (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 * |
16 |
* GNU General Public License for more details. * |
17 |
* * |
18 |
* You should have received a copy of the GNU General Public License * |
19 |
* along with this program; if not, write to the Free Software * |
20 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * |
21 |
* MA 02111-1307 USA * |
22 |
***************************************************************************/ |
23 |
|
24 |
#include "MidiInputDeviceMme.h" |
25 |
#include "MidiInputDeviceFactory.h" |
26 |
|
27 |
|
28 |
|
29 |
|
30 |
namespace LinuxSampler { |
31 |
|
32 |
void CALLBACK MidiInputDeviceMme::MidiInputPortMme::win32_midiin_callback(HMIDIIN handle, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { |
33 |
MidiInputDeviceMme::MidiInputPortMme* p = (MidiInputDeviceMme::MidiInputPortMme*)dwInstance; |
34 |
p->MmeCallbackDispatcher(handle, uMsg, dwParam1, dwParam2); |
35 |
} |
36 |
|
37 |
|
38 |
|
39 |
// *************** ParameterPorts *************** |
40 |
// * |
41 |
|
42 |
// *************** ParameterPorts *************** |
43 |
// * |
44 |
|
45 |
MidiInputDeviceMme::ParameterPorts::ParameterPorts() : DeviceCreationParameterInt() { |
46 |
InitWithDefault(); |
47 |
} |
48 |
|
49 |
MidiInputDeviceMme::ParameterPorts::ParameterPorts(String val) : DeviceCreationParameterInt(val) { |
50 |
} |
51 |
|
52 |
String MidiInputDeviceMme::ParameterPorts::Description() { |
53 |
return "Number of ports"; |
54 |
} |
55 |
|
56 |
bool MidiInputDeviceMme::ParameterPorts::Fix() { |
57 |
return true; |
58 |
} |
59 |
|
60 |
bool MidiInputDeviceMme::ParameterPorts::Mandatory() { |
61 |
return false; |
62 |
} |
63 |
|
64 |
std::map<String,DeviceCreationParameter*> MidiInputDeviceMme::ParameterPorts::DependsAsParameters() { |
65 |
return std::map<String,DeviceCreationParameter*>(); |
66 |
} |
67 |
|
68 |
// the MME driver supports only one port so to manage multiple MME MIDI ports the user just creates several MME drivers and connects each one to the desired MME port |
69 |
optional<int> MidiInputDeviceMme::ParameterPorts::DefaultAsInt(std::map<String,String> Parameters) { |
70 |
return 1; |
71 |
} |
72 |
|
73 |
optional<int> MidiInputDeviceMme::ParameterPorts::RangeMinAsInt(std::map<String,String> Parameters) { |
74 |
return 1; |
75 |
} |
76 |
|
77 |
optional<int> MidiInputDeviceMme::ParameterPorts::RangeMaxAsInt(std::map<String,String> Parameters) { |
78 |
return 1; |
79 |
} |
80 |
|
81 |
std::vector<int> MidiInputDeviceMme::ParameterPorts::PossibilitiesAsInt(std::map<String,String> Parameters) { |
82 |
return std::vector<int>(); |
83 |
} |
84 |
|
85 |
void MidiInputDeviceMme::ParameterPorts::OnSetValue(int i) throw (Exception) { |
86 |
if (i != 1) throw Exception("MME only supports one MIDI port per device"); |
87 |
} |
88 |
|
89 |
String MidiInputDeviceMme::ParameterPorts::Name() { |
90 |
return "PORTS"; |
91 |
} |
92 |
|
93 |
// the MME driver supports only one port so to manage multiple MME MIDI ports the user just creates several MME drivers and connects each one to the desired MME port |
94 |
|
95 |
|
96 |
|
97 |
// *************** ParameterPort *************** |
98 |
// * |
99 |
|
100 |
MidiInputDeviceMme::MidiInputPortMme::ParameterPort::ParameterPort(MidiInputPortMme* pPort) |
101 |
: DeviceRuntimeParameterString("") { |
102 |
this->pPort = pPort; |
103 |
} |
104 |
|
105 |
String MidiInputDeviceMme::MidiInputPortMme::ParameterPort::Description() { |
106 |
return "MME Destination MIDI Port"; |
107 |
} |
108 |
|
109 |
bool MidiInputDeviceMme::MidiInputPortMme::ParameterPort::Fix() { |
110 |
return false; |
111 |
} |
112 |
|
113 |
std::vector<String> MidiInputDeviceMme::MidiInputPortMme::ParameterPort::PossibilitiesAsString() { |
114 |
// returns a list of the available MME Input MIDI ports you can connect to |
115 |
std::vector<String> ports; |
116 |
MIDIINCAPS midiincaps; |
117 |
|
118 |
int NumDevs = midiInGetNumDevs(); |
119 |
|
120 |
for(int i=0;i<NumDevs;i++) { |
121 |
int res = midiInGetDevCaps(i, &midiincaps, sizeof(MIDIINCAPS)); |
122 |
if(res == MMSYSERR_NOERROR) { |
123 |
ports.push_back( (String)midiincaps.szPname); |
124 |
} |
125 |
} |
126 |
|
127 |
return ports; |
128 |
} |
129 |
|
130 |
void MidiInputDeviceMme::MidiInputPortMme::ParameterPort::OnSetValue(String s) { |
131 |
pPort->ConnectToMmeMidiSource(s.c_str()); |
132 |
} |
133 |
|
134 |
|
135 |
|
136 |
// *************** MidiInputPortMme *************** |
137 |
// * |
138 |
|
139 |
MidiInputDeviceMme::MidiInputPortMme::MidiInputPortMme(MidiInputDeviceMme* pDevice) throw (MidiInputException) |
140 |
: MidiInputPort(pDevice, ((DeviceCreationParameterInt*)pDevice->Parameters["PORTS"])->ValueAsInt() - 1) |
141 |
{ |
142 |
this->pDevice = pDevice; |
143 |
MidiInOpened = false; |
144 |
SysExBuf = new char[MME_MAX_SYSEX_BUF_SIZE]; |
145 |
TmpSysExBuf = new char[MME_MAX_SYSEX_BUF_SIZE]; |
146 |
ExitFlag = false; |
147 |
FirstSysExBlock = true; |
148 |
SysExMsgComplete = false; |
149 |
dmsg(3,("created MME port %d\n", this->portNumber)); |
150 |
Parameters["PORT"] = new ParameterPort(this); |
151 |
} |
152 |
|
153 |
MidiInputDeviceMme::MidiInputPortMme::~MidiInputPortMme() { |
154 |
CloseMmeMidiPort(); |
155 |
delete[] TmpSysExBuf; |
156 |
delete[] SysExBuf; |
157 |
} |
158 |
|
159 |
|
160 |
/*** |
161 |
* Closes the MME MIDI Input port in a safe way |
162 |
*/ |
163 |
void MidiInputDeviceMme::MidiInputPortMme::CloseMmeMidiPort(void) { |
164 |
int res; |
165 |
if (MidiInOpened == true) { |
166 |
ExitFlag = true; |
167 |
midiInReset(MidiInHandle); |
168 |
while ((res = midiInClose(MidiInHandle)) == MIDIERR_STILLPLAYING) Sleep(100); |
169 |
midiInUnprepareHeader(MidiInHandle, &midiHdr, sizeof(MIDIHDR)); |
170 |
MidiInOpened = false; |
171 |
} |
172 |
} |
173 |
|
174 |
/** |
175 |
* Connects the port with the MME Midi Input port |
176 |
* |
177 |
* @param Name of the MME Midi Input port e.g. "MAUDIO MIDI IN" |
178 |
* @throws MidiInputException if connection cannot be established |
179 |
*/ |
180 |
|
181 |
void MidiInputDeviceMme::MidiInputPortMme::ConnectToMmeMidiSource(const char* MidiSource) { |
182 |
|
183 |
// close the old MIDI Input port if it was already opened |
184 |
CloseMmeMidiPort(); |
185 |
|
186 |
MIDIINCAPS midiincaps; |
187 |
int NumDevs = midiInGetNumDevs(); |
188 |
|
189 |
int FoundMidiInDeviceId = -1; |
190 |
for(int i=0;i<NumDevs;i++) { |
191 |
int res = midiInGetDevCaps(i, &midiincaps, sizeof(MIDIINCAPS)); |
192 |
if(res == MMSYSERR_NOERROR) { |
193 |
if(!strcmp(midiincaps.szPname, MidiSource)) { |
194 |
FoundMidiInDeviceId = i; |
195 |
break; |
196 |
} |
197 |
} |
198 |
} |
199 |
|
200 |
if(FoundMidiInDeviceId == -1) throw MidiInputException("MIDI port connect failed"); |
201 |
|
202 |
int res; |
203 |
res = midiInOpen(&MidiInHandle, FoundMidiInDeviceId, (DWORD_PTR)win32_midiin_callback, (DWORD_PTR)this, CALLBACK_FUNCTION); |
204 |
if(res != MMSYSERR_NOERROR) { |
205 |
throw MidiInputException("MIDI port connect failed. midiInOpen error"); |
206 |
} |
207 |
|
208 |
MidiInOpened = true; |
209 |
|
210 |
/* Store pointer to our input buffer for System Exclusive messages in MIDIHDR */ |
211 |
midiHdr.lpData = &TmpSysExBuf[0]; |
212 |
|
213 |
/* Store its size in the MIDIHDR */ |
214 |
midiHdr.dwBufferLength = MME_MAX_SYSEX_BUF_SIZE; |
215 |
|
216 |
/* Flags must be set to 0 */ |
217 |
midiHdr.dwFlags = 0; |
218 |
|
219 |
/* Prepare the buffer and MIDIHDR */ |
220 |
res = midiInPrepareHeader(MidiInHandle, &midiHdr, sizeof(MIDIHDR)); |
221 |
if(res != MMSYSERR_NOERROR) { |
222 |
CloseMmeMidiPort(); |
223 |
throw MidiInputException("MIDI port connect failed. midiInPrepareHeader error"); |
224 |
} |
225 |
|
226 |
/* Queue MIDI input buffer */ |
227 |
res = midiInAddBuffer(MidiInHandle, &midiHdr, sizeof(MIDIHDR)); |
228 |
if(res != MMSYSERR_NOERROR) { |
229 |
CloseMmeMidiPort(); |
230 |
throw MidiInputException("MIDI port connect failed. midiInAddBuffer error"); |
231 |
} |
232 |
|
233 |
|
234 |
res = midiInStart(MidiInHandle); |
235 |
if(res != MMSYSERR_NOERROR) { |
236 |
CloseMmeMidiPort(); |
237 |
throw MidiInputException("MIDI port connect failed, midiInStart failed."); |
238 |
} |
239 |
|
240 |
} |
241 |
|
242 |
void MidiInputDeviceMme::MidiInputPortMme::MmeCallbackDispatcher(HMIDIIN handle, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { |
243 |
|
244 |
unsigned char *DataPtr; // pointer to SysEx data |
245 |
unsigned char *data; // pointer to standard MIDI messages which are not sysex data(max 3 bytes long) |
246 |
|
247 |
data = (unsigned char *)&dwParam1; |
248 |
|
249 |
switch(uMsg) { |
250 |
case MIM_DATA: { |
251 |
//FIXME: passing timeStamp this way here does not work, since the DispatchRaw() expects it to be in period position, not miliseconds, requires additional code in RTMath to be able to transform the value for this purpose here |
252 |
//int32_t timeStamp = dwParam2; |
253 |
//DispatchRaw(data, timeStamp); |
254 |
DispatchRaw(data); |
255 |
break; |
256 |
} |
257 |
|
258 |
case MIM_LONGDATA: |
259 |
if(!ExitFlag) { |
260 |
LPMIDIHDR lpMIDIHeader = (LPMIDIHDR)dwParam1; |
261 |
DataPtr = (unsigned char *)(lpMIDIHeader->lpData); |
262 |
if(lpMIDIHeader->dwBytesRecorded == 0) break; |
263 |
|
264 |
if(FirstSysExBlock) { |
265 |
SysExOffset = 0; |
266 |
FirstSysExBlock = false; |
267 |
} |
268 |
|
269 |
if( DataPtr[lpMIDIHeader->dwBytesRecorded - 1] == 0xF7) { |
270 |
SysExMsgComplete = true; |
271 |
FirstSysExBlock = true; |
272 |
} |
273 |
else { |
274 |
SysExMsgComplete = false; |
275 |
} |
276 |
|
277 |
if(SysExOffset + lpMIDIHeader->dwBytesRecorded <= MME_MAX_SYSEX_BUF_SIZE) { |
278 |
memcpy(&SysExBuf[SysExOffset], DataPtr, lpMIDIHeader->dwBytesRecorded); |
279 |
SysExOffset += lpMIDIHeader->dwBytesRecorded; |
280 |
} |
281 |
|
282 |
if(SysExMsgComplete) DispatchSysex(SysExBuf, SysExOffset); |
283 |
|
284 |
/* Queue the MIDIHDR for more input, only if ExitFlag was not set otherwise we risk an infinite loop |
285 |
because when we call midiInReset() below, Windows will send a final MIM_LONGDATA message to that callback. |
286 |
*/ |
287 |
midiInAddBuffer(MidiInHandle, lpMIDIHeader, sizeof(MIDIHDR)); |
288 |
} |
289 |
break; |
290 |
} |
291 |
|
292 |
} |
293 |
|
294 |
|
295 |
|
296 |
|
297 |
// *************** MidiInputDeviceMme *************** |
298 |
// * |
299 |
|
300 |
MidiInputDeviceMme::MidiInputDeviceMme(std::map<String,DeviceCreationParameter*> Parameters, void* pSampler) : MidiInputDevice(Parameters, pSampler) { |
301 |
AcquirePorts(((DeviceCreationParameterInt*)Parameters["PORTS"])->ValueAsInt()); |
302 |
if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) { |
303 |
Listen(); |
304 |
} |
305 |
} |
306 |
|
307 |
MidiInputDeviceMme::~MidiInputDeviceMme() { |
308 |
// free the midi ports (we can't let the base class do this, |
309 |
// as the MidiInputPortMme destructors need access to |
310 |
// hAlsaSeq) |
311 |
for (std::map<int,MidiInputPort*>::iterator iter = Ports.begin(); iter != Ports.end() ; iter++) { |
312 |
delete static_cast<MidiInputPortMme*>(iter->second); |
313 |
} |
314 |
Ports.clear(); |
315 |
} |
316 |
|
317 |
MidiInputDeviceMme::MidiInputPortMme* MidiInputDeviceMme::CreateMidiPort() { |
318 |
return new MidiInputPortMme(this); |
319 |
} |
320 |
|
321 |
String MidiInputDeviceMme::Name() { |
322 |
return "MME"; |
323 |
} |
324 |
|
325 |
String MidiInputDeviceMme::Driver() { |
326 |
return Name(); |
327 |
} |
328 |
|
329 |
void MidiInputDeviceMme::Listen() { |
330 |
//TODO: ... |
331 |
} |
332 |
|
333 |
void MidiInputDeviceMme::StopListen() { |
334 |
//TODO: ... |
335 |
} |
336 |
|
337 |
String MidiInputDeviceMme::Description() { |
338 |
return "MultiMedia Extensions"; |
339 |
} |
340 |
|
341 |
String MidiInputDeviceMme::Version() { |
342 |
String s = "$Revision$"; |
343 |
return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword |
344 |
} |
345 |
|
346 |
|
347 |
|
348 |
} // namespace LinuxSampler |