summaryrefslogtreecommitdiff
path: root/libs/ardour/ardour/port_engine.h
blob: 056b70f31b396cf11f3fe0ab093d403e3bc96ea4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
/*
 * Copyright (C) 2013-2018 Paul Davis <paul@linuxaudiosystems.com>
 * Copyright (C) 2015-2018 Robin Gareus <robin@gareus.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifndef __libardour_port_engine_h__
#define __libardour_port_engine_h__

#include <vector>
#include <string>

#include <stdint.h>

#include "ardour/data_type.h"
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"

namespace ARDOUR {

class PortManager;

/** PortEngine is an abstract base class that defines the functionality
 * required by Ardour.
 *
 * A Port is basically an endpoint for a datastream (which can either be
 * continuous, like audio, or event-based, like MIDI). Ports have buffers
 * associated with them into which data can be written (if they are output
 * ports) and from which data can be read (if they input ports). Ports can be
 * connected together so that data written to an output port can be read from
 * an input port. These connections can be 1:1, 1:N OR N:1.
 *
 * Ports may be associated with software only, or with hardware.  Hardware
 * related ports are often referred to as physical, and correspond to some
 * relevant physical entity on a hardware device, such as an audio jack or a
 * MIDI connector. Physical ports may be potentially asked to monitor their
 * inputs, though some implementations may not support this.
 *
 * Most physical ports will also be considered "terminal", which means that
 * data delivered there or read from there will go to or comes from a system
 * outside of the PortEngine implementation's control (e.g. the analog domain
 * for audio, or external MIDI devices for MIDI). Non-physical ports can also
 * be considered "terminal". For example, the output port of a software
 * synthesizer is a terminal port, because the data contained in its buffer
 * does not and cannot be considered to come from any other port - it is
 * synthesized by its owner.
 *
 * Ports also have latency associated with them. Each port has a playback
 * latency and a capture latency:
 *
 * <b>capture latency</b>: how long since the data read from the buffer of a
 *                  port arrived at at a terminal port.  The data will have
 *                  come from the "outside world" if the terminal port is also
 *                  physical, or will have been synthesized by the entity that
 *                  owns the terminal port.
 *
 * <b>playback latency</b>: how long until the data written to the buffer of
 *                   port will reach a terminal port.
 *
 *
 * For more detailed questions about the PortEngine API, consult the JACK API
 * documentation, on which this entire object is based.
 */

class LIBARDOUR_API ProtoPort {
  public:
	ProtoPort() {}
	virtual ~ProtoPort () {}
};

class LIBARDOUR_API PortEngine
{
public:
	PortEngine (PortManager& pm) : manager (pm) {}
	virtual ~PortEngine() {}

	/** Return a private, type-free pointer to any data
	 * that might be useful to a concrete implementation
	 */
	virtual void* private_handle() const = 0;

	/** Opaque handle to use as reference for Ports
	 *
	 * The handle needs to be lifetime managed (i.e. a shared_ptr type)
	 * in order to allow RCU to provide lock-free cross-thread operations
	 * on ports and ports containers.
	 *
	 * We could theoretically use a template (PortEngine\<T\>) and define
	 * PortHandle as T, but this complicates the desired inheritance
	 * pattern in which FooPortEngine handles things for the Foo API,
	 * rather than being a derivative of PortEngine\<Foo\>.
	 *
	 * We use this to declare return values and members of structures.
	 */
	typedef boost::shared_ptr<ProtoPort> PortPtr;

	/* We use this to declare arguments to methods/functions, in order to
	 * avoid copying shared_ptr<ProtoPort> every time (a practice we use in
	 * other contexts where we pass shared_ptr<T>). 
	 */
	typedef PortPtr const & PortHandle;

	/** Return the name of this process as used by the port manager
	 * when naming ports.
	 */
	virtual const std::string& my_name() const = 0;

	/** Return the maximum size of a port name */
	virtual uint32_t port_name_size() const = 0;

	/** Set/rename port
	 *
	 * @param port \ref PortHandle to operate on
	 * @param name new name to use for this port
	 * @return zero if successful, non-zero otherwise
	 */
	virtual int         set_port_name (PortHandle port, const std::string& name) = 0;

	/** Query port name
	 *
	 * @param port \ref PortHandle
	 * @return the name of the port referred to by @param port . If the port
	 *         does not exist, return an empty string.
	 */
	virtual std::string get_port_name (PortHandle port) const = 0;

	/** Query port-flags
	 *
	 * @param port \ref PortHandle
	 * @return the flags of the port referred to by \p port . If the port
	 *         does not exist, return PortFlags (0)
	 */
	virtual PortFlags get_port_flags (PortHandle port) const = 0;

	/** Return the port-property value and type for a given key.
	 * (eg query a human readable port name)
	 *
	 * The API follows jack_get_property():
	 *
	 * @param key The key of the property to retrieve
	 * @param value Set to the value of the property if found
	 * @param type The type of the property if set (
	 *             Type of data, either a MIME type or URI.
	 *             If type is empty, the data is assumed to be a UTF-8 encoded string.
	 *
	 * @return 0 on success, -1 if the @p subject has no @p key property.
	 *
	 * for available keys, see
	 * https://github.com/jackaudio/headers/blob/master/metadata.h
	 * https://github.com/drobilla/jackey/blob/master/jackey.h
	 */
	virtual int get_port_property (PortHandle, const std::string& key, std::string& value, std::string& type) const { return -1; }

	/** Set the port-property value and type for a given key
	 *
	 * The API follows jack_set_property():
	 * @param key The key of the property.
	 * @param value The value of the property.
	 * @param type The type of the property.
	 *
	 * @return 0 on success, -1 on error
	 */
	virtual int set_port_property (PortHandle, const std::string& key, const std::string& value, const std::string& type) { return -1; }

	/** Return a reference to a port with the fullname \p name .
	 *
	 * @param name Full port-name to lookup
	 * @return PortHandle if lookup was successful, or an "empty" PortHandle (analogous to a null pointer) if no such port exists.
	 */
	virtual PortPtr get_port_by_name (const std::string& name) const = 0;

	/** Find the set of ports whose names, types and flags match
	 * specified values, place the names of each port into \p ports .
	 *
	 * @param port_name_pattern match by given pattern. To avoid selecting by name, pass an empty string.
	 * @param type filter by given type; pass DataType::NIL to match all types.
	 * @param flags filter by flags, pass PortFlags (0) to avoid selecting by flags.
	 * @param ports array filled with matching port-names
	 * @return the count of the number found
	 */
	virtual int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>& ports) const = 0;

	/** Lookup data type of a port
	 *
	 * @param port \ref PortHandle of the port to lookup.
	 * @return the Ardour data type handled by the port referred to by \p port .
	 *         DataType::NIL is returned if the port does not exist.
	 */
	virtual DataType port_data_type (PortHandle port) const = 0;

	/** Create a new port whose fullname will be the conjunction of my_name(),
	 * ":" and \p shortname . The port will handle data specified by \p type
	 * and will have the flags given by \p flags . If successful,
	 *
	 * @param shortname Name of port to create
	 * @param type type of port to create
	 * @param flags flags of the port to create
	 * @return a reference to the port, otherwise return a null pointer.
	 */
	virtual PortPtr register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags) = 0;

	/* Destroy the port referred to by \p port, including all resources
	 * associated with it. This will also disconnect \p port from any ports it
	 * is connected to.
	 *
	 * @param port \ref PortHandle of the port to destroy
	 */
	virtual void    unregister_port (PortHandle port) = 0;

	/* Connection management */

	/** Ensure that data written to the port named by \p src will be
	 * readable from the port named by \p dst
	 *
	 * @param src name of source port to connect
	 * @param dst name of destination (sink) port
	 * @return zero on success, non-zero otherwise.
	 */
	virtual int   connect (const std::string& src, const std::string& dst) = 0;

	/** Remove any existing connection between the ports named by \p src and
	 * \p dst
	 *
	 * @param src name of source port to dis-connect to disconnect from
	 * @param dst name of destination (sink) port to disconnect
	 * @return zero on success, non-zero otherwise.
	 */
	virtual int   disconnect (const std::string& src, const std::string& dst) = 0;

	/** Ensure that data written to the port referenced by \p src will be
	 * readable from the port named by \p dst
	 *
	 * @param src \ref PortHandle of source port to connect
	 * @param dst \ref PortHandle of destination (sink) port
	 * @return zero on success, non-zero otherwise.
	 */
	virtual int   connect (PortHandle src, const std::string& dst) = 0;

	/** Remove any existing connection between the port referenced by \p src and
	 * the port named \p dst
	 *
	 * @param src \ref PortHandle of source port to disconnect from
	 * @param dst \ref PortHandle of destination (sink) port to disconnect
	 * @return zero on success, non-zero otherwise.
	 */
	virtual int   disconnect (PortHandle src, const std::string& dst) = 0;

	/** Remove all connections between the port referred to by \p port and
	 * any other ports.
	 *
	 * @param port \ref PortHandle of port to disconnect
	 * @return zero on success, non-zero otherwise.
	 */
	virtual int   disconnect_all (PortHandle port) = 0;

	/** Test if given \p port is connected
	 *
	 * @param port \ref PortHandle of port to test
	 * @param process_callback_safe true if this method is not called from rt-context of backend callbacks
	 * @return true if the port referred to by \p port has any connections to other ports. Return false otherwise.
	 */
	virtual bool  connected (PortHandle port, bool process_callback_safe = true) = 0;

	/** Test port connection
	 *
	 * @param port \ref PortHandle of source port to test
	 * @param name name of destination to test
	 * @param process_callback_safe true if this method is not called from rt-context of backend callbacks
	 * @return true if the port referred to by \p port is connected to the port named by \p name . Return false otherwise.
	 */
	virtual bool  connected_to (PortHandle port, const std::string& name, bool process_callback_safe = true) = 0;

	/** Test if given \p port is is connected to physical I/O ports.
	 *
	 * @param port \ref PortHandle of source port to test
	 * @param process_callback_safe true if this method is not called from rt-context of backend callbacks
	 * @return true if the port referred to by \p port has any connections
	 *         to ports marked with the PortFlag IsPhysical. Return false otherwise.
	 */
	virtual bool  physically_connected (PortHandle port, bool process_callback_safe = true) = 0;

	/** Test if given \p port is has external connections.
	 *
	 * @param port \ref PortHandle of port to test
	 * @param process_callback_safe true if this method is not called from rt-context of backend callbacks
	 * @return true if the port referred to by \p port has any connections
	 *         to external, not-ardour owned, ports.
	 */
	virtual bool  externally_connected (PortHandle port, bool process_callback_safe = true) {
		/* only with JACK, provides client ports that are not physical */
		return physically_connected (port, process_callback_safe);
	}

	/** Place the names of all ports connected to the port named by
	 * \p port into \p names .
	 *
	 * @param port \ref PortHandle
	 * @param names array or returned port-names
	 * @param process_callback_safe true if this method is not called from rt-context of backend callbacks
	 * @return number of connections found
	 */
	virtual int   get_connections (PortHandle port, std::vector<std::string>& names, bool process_callback_safe = true) = 0;

	/* MIDI */

	/** Retrieve a MIDI event from the data at \p port_buffer . The event
	 * number to be retrieved is given by \p event_index (a value of zero
	 * indicates that the first event in the port_buffer should be retrieved).
	 *
	 * The data associated with the event will be copied into the buffer at
	 * \p buf and the number of bytes written will be stored in \p size .
	 * The timestamp of the event (which is always relative to the start
	 * of the current process cycle, in samples) will be stored in \p timestamp .
	 *
	 * @param timestamp time in samples relative to the current cycle start
	 * @param size number of bytes read into \p buf
	 * @param buf raw MIDI data
	 * @param port_buffer the midi-port buffer
	 * @param event_index index of event to retrieve
	 * @return 0 on success, -1 otherwise
	 */
	virtual int      midi_event_get (pframes_t& timestamp, size_t& size, uint8_t const** buf, void* port_buffer, uint32_t event_index) = 0;

	/** Place a MIDI event consisting of \p size bytes copied from the data
	 * at \p buffer into the port buffer referred to by \p port_buffer .
	 * The MIDI event will be marked with a time given by \p timestamp .
	 *
	 * Events  must be added monotonically to a port buffer. An attempt to
	 * add a non-monotonic event (e.g. out-of-order) will cause this method
	 * to return a failure status.
	 *
	 * @param port_buffer the midi-port buffer
	 * @param timestamp time in samples relative to the current cycle start
	 * @param buffer raw MIDI data to emplace
	 * @param size number of bytes of \p buffer
	 * @return zero on success, non-zero otherwise.
	 */
	virtual int      midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size) = 0;

	/** Query the number of MIDI events in the data at \p port_buffer
	 *
	 * @param port_buffer the midi-port buffer
	 * @return the number of MIDI events in the data at \p port_buffer
	*/
	virtual uint32_t get_midi_event_count (void* port_buffer) = 0;

	/** Clear the buffer at \p port_buffer of all MIDI events.
	 *
	 * After a call to this method, an immediate, subsequent call to
	 * \ref get_midi_event_count with the same \p port_buffer argument must
	 * return zero.
	 *
	 * @param port_buffer the buffer to clear
	 */
	virtual void     midi_clear (void* port_buffer) = 0;

	/* Monitoring */

	/** Return true if the implementation can offer input monitoring.
	 *
	 * Input monitoring involves the (selective) routing of incoming data
	 * to an outgoing data stream, without the data being passed to the CPU.
	 *
	 * Only certain audio hardware can provide this, and only certain audio
	 * APIs can offer it.
	 */
	virtual bool  can_monitor_input() const = 0;

	/** Increment or decrement the number of requests to monitor the input
	 * of the hardware channel represented by the port referred to by
	 * \p port .
	 *
	 * If the number of requests rises above zero, input monitoring will
	 * be enabled (if can_monitor_input() returns true for the implementation).
	 *
	 * If the number of requests falls to zero, input monitoring will be
	 * disabled (if can_monitor_input() returns true for the implementation)
	 *
	 * @param port \ref PortHandle
	 * @param yn true to enable hardware monitoring, false to disable
	 * @return 0 on success, -1 otherwise
	 */
	virtual int   request_input_monitoring (PortHandle port, bool yn) = 0;

	/* Force input monitoring of the hardware channel represented by the port
	 * referred to by \p port to be on or off, depending on the true/false
	 * status of \p yn. The request count is ignored when using this
	 * method, so if this is called with \p yn set to false, input monitoring will
	 * be disabled regardless of the number of requests to enable it.
	 *
	 * @param port \ref PortHandle
	 * @param yn true to enable hardware monitoring, false to disable
	 * @return 0 on success, -1 otherwise
	 */
	virtual int   ensure_input_monitoring (PortHandle port, bool yn) = 0;

	/** Query status of hardware monitoring for given \p port
	 *
	 * @param port \ref PortHandle to test
	 * @return true if input monitoring is enabled for the hardware channel
	 *         represented by the port referred to by \p port .
	 *         Return false otherwise.
	 */
	virtual bool  monitoring_input (PortHandle port) = 0;

	/* Latency management */

	/** Set the latency range for the port referred to by \p port to
	 * \p r . The playback range will be set if \p for_playback is true,
	 * otherwise the capture range will be set.
	 *
	 * @param port \ref PortHandle to operate on
	 * @param for_playback When true, playback latency is set: How long will it be
	 *                     until the signal arrives at the edge of the process graph.
	 *                     When false the capture latency is set: ow long has it been
	 *                     since the signal arrived at the edge of the process graph.
	 * @param r min/max latency for given port.
	 */
	virtual void          set_latency_range (PortHandle port, bool for_playback, LatencyRange r) = 0;

	/** Return the latency range for the port referred to by \p port .
	 * The playback range will be returned if @param for_playback is true,
	 * otherwise the capture range will be returned.
	 *
	 * @param port The PortHandle to query
	 * @param for_playback When true, playback (downstream) latency is queried,
	 *                     false for capture (upstream) latency.
	 */
	virtual LatencyRange  get_latency_range (PortHandle port, bool for_playback) = 0;

	/* Discovering physical ports */

	/** Return true if the port referred to by \p port has the IsPhysical
	 * flag set. Return false otherwise.
	 *
	 * @param port \ref PortHandle to query
	 */
	virtual bool      port_is_physical (PortHandle port) const = 0;

	/** Store into \p names the names of all ports with the IsOutput and
	 * IsPhysical flag set, that handle data of type \p type .
	 *
	 * This can be used to discover outputs associated with hardware devices.
	 *
	 * @param type Data-type to lookup
	 * @param names return value to populate with names
	 */
	virtual void      get_physical_outputs (DataType type, std::vector<std::string>& names) = 0;

	/** Store into @param names the names of all ports with the IsInput and
	 * IsPhysical flags set, that handle data of type @param type .
	 *
	 * This can be used to discover inputs associated with hardware devices.
	 */
	virtual void      get_physical_inputs (DataType type, std::vector<std::string>& names) = 0;

	/** @return the total count (possibly mixed between different data types)
	 * of the number of ports with the IsPhysical and IsOutput flags set.
	 */
	virtual ChanCount n_physical_outputs () const = 0;

	/** @return the total count (possibly mixed between different data types)
	 * of the number of ports with the IsPhysical and IsInput flags set.
	 */
	virtual ChanCount n_physical_inputs () const = 0;

	/** Return the address of the memory area where data for the port can be
	 * written (if the port has the PortFlag IsOutput set) or read (if the port
	 * has the PortFlag IsInput set).
	 *
	 * The return value is untyped because buffers containing different data
	 * depending on the port type.
	 *
	 * @param port \ref PortHandle
	 * @param off memory offset
	 * @return pointer to raw memory area
	 */
	virtual void* get_buffer (PortHandle port, pframes_t off) = 0;

	/* MIDI ports (the ones in libmidi++) need this to be able to correctly
	 * schedule MIDI events within their buffers. It is a bit odd that we
	 * expose this here, because it is also exposed by AudioBackend, but they
	 * only have access to a PortEngine object, not an AudioBackend.
	 *
	 * Return the time according to the sample clock in use when the current
	 * buffer process cycle began.
	 *
	 * XXX to be removed after some more design cleanup.
	 */
	virtual samplepos_t sample_time_at_cycle_start () = 0;

protected:
	PortManager& manager;
};

} // namespace

#endif /* __libardour_port_engine_h__ */