/[svn]/linuxsampler/trunk/src/network/lscpserver.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/network/lscpserver.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2135 - (show annotations) (download) (as text)
Thu Sep 30 20:00:43 2010 UTC (13 years, 7 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 23335 byte(s)
* added and implemented a set of 19 new LSCP commands for controlling
  internal effects:
  - added LSCP command "GET AVAILABLE_EFFECTS"
  - added LSCP command "LIST AVAILABLE_EFFECTS"
  - added LSCP command "GET EFFECT INFO <effect-index>"
  - added LSCP command "CREATE EFFECT_INSTANCE <effect-index>"
  - added LSCP command
    "CREATE EFFECT_INSTANCE <effect-system> <module> <effect-name>"
  - added LSCP command "DESTROY EFFECT_INSTANCE <effect-instance>"
  - added LSCP command "GET EFFECT_INSTANCES"
  - added LSCP command "LIST EFFECT_INSTANCES"
  - added LSCP command "GET EFFECT_INSTANCE INFO <effect-instance>"
  - added LSCP command "GET EFFECT_INSTANCE_INPUT_CONTROL INFO
    <effect-instance> <input-control>"
  - added LSCP command "SET EFFECT_INSTANCE_INPUT_CONTROL <effect-instance>
    <input-control> <value>"
  - added LSCP command "GET MASTER_EFFECT_CHAINS <audio-device>"
  - added LSCP command "LIST MASTER_EFFECT_CHAINS <audio-device>"
  - added LSCP command "ADD MASTER_EFFECT_CHAIN <audio-device>"
  - added LSCP command
    "REMOVE MASTER_EFFECT_CHAIN <audio-device> <effect-chain>"
  - added LSCP command
    "GET MASTER_EFFECT_CHAIN INFO <audio-device> <effect-chain>"
  - added LSCP command "APPEND MASTER_EFFECT_CHAIN EFFECT <audio-device>
    <effect-chain> <effect-instance>"
  - added LSCP command "INSERT MASTER_EFFECT_CHAIN EFFECT <audio-device>
    <effect-chain> <effect-instance> <effect-chain-pos>"
  - added LSCP command "REMOVE MASTER_EFFECT_CHAIN EFFECT <audio-device>
    <effect-chain> <effect-instance>"
* bumped version to 1.0.0.cvs7

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2008 Christian Schoenebeck *
7 * *
8 * This library 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 library 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 library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #ifndef __LSCPSERVER_H_
25 #define __LSCPSERVER_H_
26
27 #if defined(WIN32)
28 #include <windows.h>
29 typedef int socklen_t;
30 #else
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <sys/time.h>
36 #include <netinet/in.h>
37 #include <netinet/tcp.h>
38 #include <arpa/inet.h>
39 #include <netdb.h>
40 #endif
41
42 #include <list>
43
44 #include "lscp.h"
45 #include "lscpparser.h"
46 #include "lscpevent.h"
47 #include "../Sampler.h"
48 #include "../common/Thread.h"
49 #include "../common/Mutex.h"
50 #include "../common/Condition.h"
51 #include "../common/global_private.h"
52
53 #include "../drivers/midi/MidiInstrumentMapper.h"
54 #include "../drivers/midi/VirtualMidiDevice.h"
55
56 #if HAVE_SQLITE3
57 #include "../db/InstrumentsDb.h"
58 #endif
59
60 /// TCP Port on which the server should listen for connection requests.
61 #define LSCP_ADDR INADDR_ANY
62 #define LSCP_PORT 8888
63
64 /// try up to 3 minutes to bind server socket
65 #define LSCP_SERVER_BIND_TIMEOUT 180
66
67 // External references to the main scanner and parser functions
68 extern int yyparse(void* YYPARSE_PARAM);
69
70 namespace LinuxSampler {
71
72 extern void restart(yyparse_param_t* pparam, int& yychar);
73
74 /**
75 * Network server for the LinuxSampler Control Protocol (LSCP).
76 */
77 class LSCPServer : public Thread {
78 public:
79 LSCPServer(Sampler* pSampler, long int addr, short int port);
80 virtual ~LSCPServer();
81 int WaitUntilInitialized(long TimeoutSeconds = 0L, long TimeoutNanoSeconds = 0L);
82 void RemoveListeners();
83
84 // Methods called by the parser
85 String DestroyAudioOutputDevice(uint DeviceIndex);
86 String DestroyMidiInputDevice(uint DeviceIndex);
87 String LoadInstrument(String Filename, uint uiInstrument, uint uiSamplerChannel, bool bBackground = false);
88 String SetEngineType(String EngineName, uint uiSamplerChannel);
89 String GetChannels();
90 String ListChannels();
91 String AddChannel();
92 String RemoveChannel(uint uiSamplerChannel);
93 String GetAvailableEngines();
94 String ListAvailableEngines();
95 String GetEngineInfo(String EngineName);
96 String GetChannelInfo(uint uiSamplerChannel);
97 String GetVoiceCount(uint uiSamplerChannel);
98 String GetStreamCount(uint uiSamplerChannel);
99 String GetBufferFill(fill_response_t ResponseType, uint uiSamplerChannel);
100 String GetAvailableAudioOutputDrivers();
101 String ListAvailableAudioOutputDrivers();
102 String GetAvailableMidiInputDrivers();
103 String ListAvailableMidiInputDrivers();
104 String GetAudioOutputDriverInfo(String Driver);
105 String GetMidiInputDriverInfo(String Driver);
106 #ifdef __GNUC__
107 typedef std::map<String,String> StringMap; // nasty workaround for a GCC bug (see GCC bug #15980, #57)
108 String GetAudioOutputDriverParameterInfo(String Driver, String Parameter, std::map<String,String> DependencyList = StringMap());
109 String GetMidiInputDriverParameterInfo(String Driver, String Parameter, std::map<String,String> DependencyList = StringMap());
110 String CreateAudioOutputDevice(String Driver, std::map<String,String> Parameters = StringMap());
111 String CreateMidiInputDevice(String Driver, std::map<String,String> Parameters = StringMap());
112 #else
113 String GetAudioOutputDriverParameterInfo(String Driver, String Parameter, std::map<String,String> DependencyList = std::map<String,String>());
114 String GetMidiInputDriverParameterInfo(String Driver, String Parameter, std::map<String,String> DependencyList = std::map<String,String>());
115 String CreateAudioOutputDevice(String Driver, std::map<String,String> Parameters = std::map<String,String>());
116 String CreateMidiInputDevice(String Driver, std::map<String,String> Parameters = std::map<String,String>());
117 #endif // __GNUC__
118 String GetAudioOutputDeviceCount();
119 String GetMidiInputDeviceCount();
120 String GetAudioOutputDevices();
121 String GetMidiInputDevices();
122 String GetAudioOutputDeviceInfo(uint DeviceIndex);
123 String GetMidiInputDeviceInfo(uint DeviceIndex);
124 String GetMidiInputPortInfo(uint DeviceIndex, uint PortIndex);
125 String GetMidiInputPortParameterInfo(uint DeviceId, uint PortId, String ParameterName);
126 String GetAudioOutputChannelInfo(uint DeviceId, uint ChannelId);
127 String GetAudioOutputChannelParameterInfo(uint DeviceId, uint ChannelId, String ParameterName);
128 String SetAudioOutputChannelParameter(uint DeviceId, uint ChannelId, String ParamKey, String ParamVal);
129 String SetAudioOutputDeviceParameter(uint DeviceIndex, String ParamKey, String ParamVal);
130 String SetMidiInputDeviceParameter(uint DeviceIndex, String ParamKey, String ParamVal);
131 String SetMidiInputPortParameter(uint DeviceIndex, uint PortIndex, String ParamKey, String ParamVal);
132 String SetAudioOutputChannel(uint ChannelAudioOutputChannel, uint AudioOutputDeviceInputChannel, uint uiSamplerChannel);
133 String SetAudioOutputDevice(uint AudioDeviceId, uint SamplerChannel);
134 String SetAudioOutputType(String AudioOutputDriver, uint uiSamplerChannel);
135 String SetMIDIInputPort(uint MIDIPort, uint uiSamplerChannel);
136 String SetMIDIInputChannel(uint MIDIChannel, uint uiSamplerChannel);
137 String SetMIDIInputDevice(uint MIDIDeviceId, uint uiSamplerChannel);
138 String SetMIDIInputType(String MidiInputDriver, uint uiSamplerChannel);
139 String SetMIDIInput(uint MIDIDeviceId, uint MIDIPort, uint MIDIChannel, uint uiSamplerChannel);
140 String SetVolume(double dVolume, uint uiSamplerChannel);
141 String SetChannelMute(bool bMute, uint uiSamplerChannel);
142 String SetChannelSolo(bool bSolo, uint uiSamplerChannel);
143 String AddOrReplaceMIDIInstrumentMapping(uint MidiMapID, uint MidiBank, uint MidiProg, String EngineType, String InstrumentFile, uint InstrumentIndex, float Volume, MidiInstrumentMapper::mode_t LoadMode, String Name, bool bModal);
144 String RemoveMIDIInstrumentMapping(uint MidiMapID, uint MidiBank, uint MidiProg);
145 String GetMidiInstrumentMappings(uint MidiMapID);
146 String GetAllMidiInstrumentMappings();
147 String GetMidiInstrumentMapping(uint MidiMapID, uint MidiBank, uint MidiProg);
148 String ListMidiInstrumentMappings(uint MidiMapID);
149 String ListAllMidiInstrumentMappings();
150 String ClearMidiInstrumentMappings(uint MidiMapID);
151 String ClearAllMidiInstrumentMappings();
152 String AddMidiInstrumentMap(String MapName = "");
153 String RemoveMidiInstrumentMap(uint MidiMapID);
154 String RemoveAllMidiInstrumentMaps();
155 String GetMidiInstrumentMaps();
156 String ListMidiInstrumentMaps();
157 String GetMidiInstrumentMap(uint MidiMapID);
158 String SetMidiInstrumentMapName(uint MidiMapID, String NewName);
159 String SetChannelMap(uint uiSamplerChannel, int MidiMapID);
160 String CreateFxSend(uint uiSamplerChannel, uint MidiCtrl, String Name = "");
161 String DestroyFxSend(uint uiSamplerChannel, uint FxSendID);
162 String GetFxSends(uint uiSamplerChannel);
163 String ListFxSends(uint uiSamplerChannel);
164 String GetFxSendInfo(uint uiSamplerChannel, uint FxSendID);
165 String SetFxSendName(uint uiSamplerChannel, uint FxSendID, String Name);
166 String SetFxSendAudioOutputChannel(uint uiSamplerChannel, uint FxSendID, uint FxSendChannel, uint DeviceChannel);
167 String SetFxSendMidiController(uint uiSamplerChannel, uint FxSendID, uint MidiController);
168 String SetFxSendLevel(uint uiSamplerChannel, uint FxSendID, double dLevel);
169
170 // effect commands
171 String GetAvailableEffects();
172 String ListAvailableEffects();
173 String GetEffectInfo(int iEffectIndex);
174 String CreateEffectInstance(int index);
175 String CreateEffectInstance(String effectSystem, String module, String effectName);
176 String DestroyEffectInstance(int iEffectID);
177 String GetEffectInstances();
178 String ListEffectInstances();
179 String GetEffectInstanceInfo(int iEffectInstanceIndex);
180 String GetEffectInstanceInputControlInfo(int iEffectInstanceIndex, int iInputControlIndex);
181 String SetEffectInstanceInputControl(int iEffectInstanceIndex, int iInputControlIndex, double dValue);
182 String GetMasterEffectChains(int iAudioOutputDevice);
183 String ListMasterEffectChains(int iAudioOutputDevice);
184 String AddMasterEffectChain(int iAudioOutputDevice);
185 String RemoveMasterEffectChain(int iAudioOutputDevice, int iMasterEffectChain);
186 String GetMasterEffectChainInfo(int iAudioOutputDevice, int iMasterEffectChain);
187 String AppendMasterEffectChainEffect(int iAudioOutputDevice, int iMasterEffectChain, int iEffectInstance);
188 String InsertMasterEffectChainEffect(int iAudioOutputDevice, int iMasterEffectChain, int iEffectInstance, int iEffectChainPosition);
189 String RemoveMasterEffectChainEffect(int iAudioOutputDevice, int iMasterEffectChain, int iEffectInstance);
190
191 String AddDbInstrumentDirectory(String Dir);
192 String RemoveDbInstrumentDirectory(String Dir, bool Force = false);
193 String GetDbInstrumentDirectoryCount(String Dir, bool Recursive = false);
194 String GetDbInstrumentDirectories(String Dir, bool Recursive = false);
195 String GetDbInstrumentDirectoryInfo(String Dir);
196 String SetDbInstrumentDirectoryName(String Dir, String Name);
197 String MoveDbInstrumentDirectory(String Dir, String Dst);
198 String CopyDbInstrumentDirectory(String Dir, String Dst);
199 String SetDbInstrumentDirectoryDescription(String Dir, String Desc);
200 String FindDbInstrumentDirectories(String Dir, std::map<String,String> Parameters, bool Recursive = true);
201 String AddDbInstruments(String DbDir, String FilePath, int Index = -1, bool bBackground = false);
202 String AddDbInstruments(String ScanMode, String DbDir, String FsDir, bool bBackground = false, bool insDir = false);
203 String RemoveDbInstrument(String Instr);
204 String GetDbInstrumentCount(String Dir, bool Recursive = false);
205 String GetDbInstruments(String Dir, bool Recursive = false);
206 String GetDbInstrumentInfo(String Instr);
207 String SetDbInstrumentName(String Instr, String Name);
208 String MoveDbInstrument(String Instr, String Dst);
209 String CopyDbInstrument(String Instr, String Dst);
210 String SetDbInstrumentDescription(String Instr, String Desc);
211 String SetDbInstrumentFilePath(String OldPath, String NewPath);
212 String FindLostDbInstrumentFiles();
213 String FindDbInstruments(String Dir, std::map<String,String> Parameters, bool Recursive = true);
214 String FormatInstrumentsDb();
215 String EditSamplerChannelInstrument(uint uiSamplerChannel);
216 String GetDbInstrumentsJobInfo(int JobId);
217 String ResetChannel(uint uiSamplerChannel);
218 String ResetSampler();
219 String GetServerInfo();
220 String GetTotalStreamCount();
221 String GetTotalVoiceCount();
222 String GetTotalVoiceCountMax();
223 String GetGlobalMaxVoices();
224 String SetGlobalMaxVoices(int iVoices);
225 String GetGlobalMaxStreams();
226 String SetGlobalMaxStreams(int iStreams);
227 String GetGlobalVolume();
228 String SetGlobalVolume(double dVolume);
229 String GetFileInstruments(String Filename);
230 String ListFileInstruments(String Filename);
231 String GetFileInstrumentInfo(String Filename, uint InstrumentID);
232 String SendChannelMidiData(String MidiMsg, uint uiSamplerChannel, uint Arg1, uint Arg2);
233 String SubscribeNotification(LSCPEvent::event_t);
234 String UnsubscribeNotification(LSCPEvent::event_t);
235 String SetEcho(yyparse_param_t* pSession, double boolean_value);
236 void AnswerClient(String ReturnMessage);
237 void CloseAllConnections();
238
239 static int currentSocket;
240 static std::map<int,String> bufferedCommands;
241
242 static void SendLSCPNotify( LSCPEvent Event );
243 static int EventSubscribers( std::list<LSCPEvent::event_t> events );
244 static void LockRTNotify();
245 static void UnlockRTNotify();
246 static String FilterEndlines(String s);
247
248 protected:
249 int hSocket;
250 sockaddr_in SocketAddress;
251 Sampler* pSampler;
252 Condition Initialized;
253
254 int Main(); ///< Implementation of virtual method from class Thread
255
256 private:
257
258 /**
259 * Find a created audio output device index.
260 */
261 int GetAudioOutputDeviceIndex (AudioOutputDevice *pDevice);
262
263 /**
264 * Find a created midi input device index.
265 */
266 int GetMidiInputDeviceIndex (MidiInputDevice *pDevice);
267
268 EngineChannel* GetEngineChannel(uint uiSamplerChannel);
269
270 /**
271 * Gets the specified effect send on the specified sampler channel.
272 */
273 FxSend* GetFxSend(uint uiSamplerChannel, uint FxSendID);
274
275 bool HasSoloChannel();
276 void MuteNonSoloChannels();
277 void UnmuteChannels();
278
279 /**
280 * Throws an exception if the specified file is not found or
281 * if directory is specified.
282 */
283 static void VerifyFile(String Filename);
284
285 static std::map<int,String> bufferedNotifies;
286 static Mutex NotifyMutex;
287 static Mutex NotifyBufferMutex;
288 static bool GetLSCPCommand( std::vector<yyparse_param_t>::iterator iter );
289 static void CloseConnection( std::vector<yyparse_param_t>::iterator iter );
290 static std::vector<yyparse_param_t> Sessions;
291 static Mutex SubscriptionMutex;
292 static std::map< LSCPEvent::event_t, std::list<int> > eventSubscriptions;
293 static fd_set fdSet;
294
295 //Protect main thread that generates real time notify messages
296 //like voice count, stream count and buffer fill
297 //from LSCP server removing engines and channels from underneath
298 static Mutex RTNotifyMutex;
299
300 class EventHandler : public ChannelCountListener, public AudioDeviceCountListener,
301 public MidiDeviceCountListener, public MidiInstrumentCountListener,
302 public MidiInstrumentInfoListener, public MidiInstrumentMapCountListener,
303 public MidiInstrumentMapInfoListener, public FxSendCountListener,
304 public VoiceCountListener, public StreamCountListener, public BufferFillListener,
305 public TotalStreamCountListener, public TotalVoiceCountListener,
306 public EngineChangeListener, public MidiPortCountListener {
307
308 public:
309 EventHandler(LSCPServer* pParent);
310
311 /**
312 * Invoked when the number of sampler channels has changed.
313 * @param NewCount The new number of sampler channels.
314 */
315 virtual void ChannelCountChanged(int NewCount);
316 virtual void ChannelAdded(SamplerChannel* pChannel);
317 virtual void ChannelToBeRemoved(SamplerChannel* pChannel);
318
319 /**
320 * Invoked when the number of audio output devices has changed.
321 * @param NewCount The new number of audio output devices.
322 */
323 virtual void AudioDeviceCountChanged(int NewCount);
324
325 /**
326 * Invoked when the number of MIDI input devices has changed.
327 * @param NewCount The new number of MIDI input devices.
328 */
329 virtual void MidiDeviceCountChanged(int NewCount);
330
331 /**
332 * Invoked right before the supplied MIDI input device is going
333 * to be destroyed.
334 * @param pDevice MidiInputDevice to be deleted
335 */
336 virtual void MidiDeviceToBeDestroyed(MidiInputDevice* pDevice);
337
338 /**
339 * Invoked to inform that a new MidiInputDevice has just been
340 * created.
341 * @param pDevice newly created MidiInputDevice
342 */
343 virtual void MidiDeviceCreated(MidiInputDevice* pDevice);
344
345 /**
346 * Invoked when the number of MIDI input ports has changed.
347 * @param NewCount The new number of MIDI input ports.
348 */
349 virtual void MidiPortCountChanged(int NewCount);
350
351 /**
352 * Invoked right before the supplied MIDI input port is going
353 * to be destroyed.
354 * @param pPort MidiInputPort to be deleted
355 */
356 virtual void MidiPortToBeRemoved(MidiInputPort* pPort);
357
358 /**
359 * Invoked to inform that a new MidiInputPort has just been
360 * added.
361 * @param pPort newly created MidiInputPort
362 */
363 virtual void MidiPortAdded(MidiInputPort* pPort);
364
365 /**
366 * Invoked when the number of MIDI instruments has changed.
367 * @param MapId The numerical ID of the MIDI instrument map.
368 * @param NewCount The new number of MIDI instruments.
369 */
370 virtual void MidiInstrumentCountChanged(int MapId, int NewCount);
371
372 /**
373 * Invoked when a MIDI instrument in a MIDI instrument map is changed.
374 * @param MapId The numerical ID of the MIDI instrument map.
375 * @param Bank The index of the MIDI bank, containing the instrument.
376 * @param Program The MIDI program number of the instrument.
377 */
378 virtual void MidiInstrumentInfoChanged(int MapId, int Bank, int Program);
379
380 /**
381 * Invoked when the number of MIDI instrument maps has changed.
382 * @param NewCount The new number of MIDI instruments.
383 */
384 virtual void MidiInstrumentMapCountChanged(int NewCount);
385
386 /**
387 * Invoked when the settings of a MIDI instrument map are changed.
388 * @param MapId The numerical ID of the MIDI instrument map.
389 */
390 virtual void MidiInstrumentMapInfoChanged(int MapId);
391
392 /**
393 * Invoked when the number of effect sends
394 * on the specified sampler channel has changed.
395 * @param ChannelId The numerical ID of the sampler channel.
396 * @param NewCount The new number of effect sends.
397 */
398 virtual void FxSendCountChanged(int ChannelId, int NewCount);
399
400 /**
401 * Invoked when the number of active voices
402 * on the specified sampler channel has changed.
403 * @param ChannelId The numerical ID of the sampler channel.
404 * @param NewCount The new number of active voices.
405 */
406 virtual void VoiceCountChanged(int ChannelId, int NewCount);
407
408 /**
409 * Invoked when the number of active disk streams
410 * on the specified sampler channel has changed.
411 * @param ChannelId The numerical ID of the sampler channel.
412 * @param NewCount The new number of active disk streams.
413 */
414 virtual void StreamCountChanged(int ChannelId, int NewCount);
415
416 /**
417 * Invoked when the fill state of the disk stream
418 * buffers on the specified sampler channel is changed.
419 * @param ChannelId The numerical ID of the sampler channel.
420 * @param FillData The buffer fill data for the specified sampler channel.
421 */
422 virtual void BufferFillChanged(int ChannelId, String FillData);
423
424 /**
425 * Invoked when the total number of active voices is changed.
426 * @param NewCount The new number of active voices.
427 */
428 virtual void TotalVoiceCountChanged(int NewCount);
429 virtual void TotalStreamCountChanged(int NewCount);
430
431 virtual void EngineToBeChanged(int ChannelId);
432 virtual void EngineChanged(int ChannelId);
433
434 virtual ~EventHandler();
435
436 struct midi_listener_entry {
437 SamplerChannel* pSamplerChannel;
438 EngineChannel* pEngineChannel;
439 VirtualMidiDevice* pMidiListener;
440 };
441
442 std::vector<midi_listener_entry> channelMidiListeners;
443
444 struct device_midi_listener_entry {
445 MidiInputPort* pPort;
446 VirtualMidiDevice* pMidiListener;
447 uint uiDeviceID;
448 };
449
450 std::vector<device_midi_listener_entry> deviceMidiListeners;
451
452 private:
453 LSCPServer* pParent;
454 } eventHandler;
455
456 #if HAVE_SQLITE3
457 class DbInstrumentsEventHandler : public InstrumentsDb::Listener {
458 public:
459 virtual void DirectoryCountChanged(String Dir);
460 virtual void DirectoryInfoChanged(String Dir);
461 virtual void DirectoryNameChanged(String Dir, String NewName);
462 virtual void InstrumentCountChanged(String Dir);
463 virtual void InstrumentInfoChanged(String Instr);
464 virtual void InstrumentNameChanged(String Instr, String NewName);
465 virtual void JobStatusChanged(int JobId);
466 } dbInstrumentsEventHandler;
467 #endif // HAVE_SQLITE3
468 };
469
470 }
471
472 #endif // __LSCPSERVER_H_

  ViewVC Help
Powered by ViewVC