1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 13 мая 2016 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins 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 Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #ifndef COINTAINER_JACK_PORTS_H_
23 #define COINTAINER_JACK_PORTS_H_
24 
25 namespace lsp
26 {
27     class JACKPort: public IPort
28     {
29         protected:
30             JACKWrapper        *pWrapper;
31 
32         public:
JACKPort(const port_t * meta,JACKWrapper * w)33             explicit JACKPort(const port_t *meta, JACKWrapper *w): IPort(meta)
34             {
35                 pWrapper        = w;
36             }
37 
~JACKPort()38             virtual ~JACKPort()
39             {
40                 pWrapper        = NULL;
41             }
42 
43         public:
init()44             virtual int init()
45             {
46                 return STATUS_OK;
47             }
48 
destroy()49             virtual void destroy()
50             {
51             }
52     };
53 
54     class JACKPortGroup: public JACKPort
55     {
56         private:
57             float                   nCurrRow;
58             size_t                  nCols;
59             size_t                  nRows;
60 
61         public:
JACKPortGroup(const port_t * meta,JACKWrapper * w)62             explicit JACKPortGroup(const port_t *meta, JACKWrapper *w) : JACKPort(meta, w)
63             {
64                 nCurrRow            = meta->start;
65                 nCols               = port_list_size(meta->members);
66                 nRows               = list_size(meta->items);
67             }
68 
~JACKPortGroup()69             virtual ~JACKPortGroup()
70             {
71                 nCurrRow            = 0;
72                 nCols               = 0;
73                 nRows               = 0;
74             }
75 
76         public:
setValue(float value)77             virtual void setValue(float value)
78             {
79                 int32_t v = value;
80                 if ((v >= 0) && (v < ssize_t(nRows)))
81                     nCurrRow        = v;
82             }
83 
getValue()84             virtual float getValue()
85             {
86                 return nCurrRow;
87             }
88 
89         public:
rows()90             inline size_t rows() const      { return nRows; }
cols()91             inline size_t cols() const      { return nCols; }
curr_row()92             inline size_t curr_row() const  { return nCurrRow; }
93     };
94 
95     class JACKDataPort: public JACKPort
96     {
97         private:
98             jack_port_t    *pPort;              // JACK port descriptor
99             void           *pDataBuffer;        // Real data buffer passed from JACK
100             void           *pBuffer;            // Data buffer
101             midi_t         *pMidi;              // Midi buffer for operating MIDI messages
102             float          *pSanitized;         // Input float data for sanitized buffers
103             size_t          nBufSize;           // Size of sanitized buffer in samples
104 
105         public:
JACKDataPort(const port_t * meta,JACKWrapper * w)106             explicit JACKDataPort(const port_t *meta, JACKWrapper *w) : JACKPort(meta, w)
107             {
108                 pPort       = NULL;
109                 pDataBuffer = NULL;
110                 pBuffer     = NULL;
111                 pMidi       = NULL;
112                 pSanitized  = NULL;
113                 nBufSize    = 0;
114             }
115 
~JACKDataPort()116             virtual ~JACKDataPort()
117             {
118                 pPort       = NULL;
119                 pDataBuffer = NULL;
120                 pBuffer     = NULL;
121                 pMidi       = NULL;
122                 pSanitized  = NULL;
123                 nBufSize    = 0;
124             };
125 
126         public:
getBuffer()127             virtual void *getBuffer()
128             {
129                 return pBuffer;
130             };
131 
init()132             virtual int init()
133             {
134                 return connect();
135             }
136 
disconnect()137             int disconnect()
138             {
139                 if (pPort == NULL)
140                     return STATUS_OK;
141 
142                 jack_client_t *cl   = pWrapper->client();
143                 if (cl != NULL)
144                     jack_port_unregister(cl, pPort);
145 
146                 if (pSanitized != NULL)
147                 {
148                     ::free(pSanitized);
149                     pSanitized = NULL;
150                 }
151 
152                 if (pMidi != NULL)
153                 {
154                     delete pMidi;
155                     pMidi       = NULL;
156                 }
157 
158                 pPort       = NULL;
159                 nBufSize    = 0;
160 
161                 return STATUS_OK;
162             }
163 
connect()164             int connect()
165             {
166                 // Determine port type
167                 const char *port_type = NULL;
168                 if (pMetadata->role == R_AUDIO)
169                     port_type = JACK_DEFAULT_AUDIO_TYPE;
170                 else if (pMetadata->role == R_MIDI)
171                 {
172                     port_type   = JACK_DEFAULT_MIDI_TYPE;
173                     pMidi       = new midi_t;
174                     if (pMidi == NULL)
175                         return STATUS_NO_MEM;
176                     pMidi->clear();
177                 }
178                 else
179                     return STATUS_BAD_FORMAT;
180 
181                 // Determine flags
182                 size_t flags = (IS_OUT_PORT(pMetadata)) ? JackPortIsOutput : JackPortIsInput;
183 
184                 // Get client
185                 jack_client_t *cl   = pWrapper->client();
186                 if (cl == NULL)
187                 {
188                     if (pMidi != NULL)
189                     {
190                         delete pMidi;
191                         pMidi   = NULL;
192                     }
193                     return STATUS_DISCONNECTED;
194                 }
195 
196                 // Register port
197                 pPort       = jack_port_register(cl, pMetadata->id, port_type, flags, 0);
198 
199                 return (pPort != NULL) ? STATUS_OK : STATUS_UNKNOWN_ERR;
200             }
201 
set_buffer_size(size_t size)202             void set_buffer_size(size_t size)
203             {
204                 // set_buffer_size should affect only input audio ports currently
205                 if ((!IS_IN_PORT(pMetadata)) || (pMidi != NULL))
206                     return;
207 
208                 // Buffer size has changed?
209                 if (nBufSize == size)
210                     return;
211 
212                 float *buf  = reinterpret_cast<float *>(::realloc(pSanitized, sizeof(float) * size));
213                 if (buf == NULL)
214                 {
215                     ::free(pSanitized);
216                     pSanitized = NULL;
217                     return;
218                 }
219 
220                 nBufSize    = size;
221                 pSanitized  = buf;
222                 dsp::fill_zero(pSanitized, nBufSize);
223             }
224 
report_latency(ssize_t latency)225             void report_latency(ssize_t latency)
226             {
227                 // Only output ports should report latency
228                 if ((pMetadata == NULL) || (!IS_OUT_PORT(pMetadata)))
229                     return;
230 
231                 // Report latency
232                 jack_latency_range_t range;
233                 jack_port_get_latency_range(pPort, JackCaptureLatency, &range);
234                 range.min += latency;
235                 range.max += latency;
236                 jack_port_set_latency_range (pPort, JackCaptureLatency, &range);
237             }
238 
destroy()239             virtual void destroy()
240             {
241                 disconnect();
242             }
243 
pre_process(size_t samples)244             virtual bool pre_process(size_t samples)
245             {
246                 if (pPort == NULL)
247                 {
248                     pBuffer     = NULL;
249                     return false;
250                 }
251 
252                 pDataBuffer = jack_port_get_buffer(pPort, samples);
253                 pBuffer     = pDataBuffer;
254 
255                 if (pMidi != NULL)
256                 {
257                     if ((pBuffer != NULL) && IS_IN_PORT(pMetadata))
258                     {
259                         // Clear our buffer
260                         pMidi->clear();
261 
262                         // Read MIDI events
263                         jack_midi_event_t   midi_event;
264                         midi::event_t       ev;
265 
266                         jack_nframes_t event_count = jack_midi_get_event_count(pBuffer);
267                         for (jack_nframes_t i=0; i<event_count; i++)
268                         {
269                             // Read MIDI event
270                             if (jack_midi_event_get(&midi_event, pBuffer, i) != 0)
271                             {
272                                 lsp_warn("Could not fetch MIDI event #%d from JACK port", int(i));
273                                 continue;
274                             }
275 
276                             // Convert MIDI event
277                             lsp_dumpb("in midi event", midi_event.buffer, midi_event.size);
278                             if (midi::decode(&ev, midi_event.buffer) <= 0)
279                             {
280                                 lsp_warn("Could not decode MIDI event #%d at timestamp %d from JACK port", int(i), int(midi_event.time));
281                                 continue;
282                             }
283 
284                             // Update timestamp and store event
285                             ev.timestamp    = midi_event.time;
286                             if (!pMidi->push(ev))
287                                 lsp_warn("Could not append MIDI event #%d at timestamp %d due to buffer overflow", int(i), int(midi_event.time));
288                         }
289 
290                         // All MIDI events ARE ordered chronologically, we do not need to perform sort
291                         #ifdef LSP_TRACE
292                             if (event_count > 0)
293                                 lsp_trace("Decoded %d MIDI events", int(event_count));
294                         #endif
295                     }
296 
297                     // Replace pBuffer with pMidi
298                     pBuffer     = pMidi;
299                 }
300                 else if (pSanitized != NULL) // Need to sanitize?
301                 {
302                     // Perform sanitize() if possible
303                     if (samples <= nBufSize)
304                     {
305                         dsp::sanitize2(pSanitized, reinterpret_cast<float *>(pDataBuffer), samples);
306                         pBuffer = pSanitized;
307                     }
308                     else
309                     {
310                         lsp_warn("Could not sanitize buffer data for port %s, not enough buffer size (required: %d, actual: %d)",
311                                 pMetadata->id, int(samples), int(nBufSize));
312                     }
313                 }
314 
315                 return false;
316             }
317 
post_process(size_t samples)318             virtual void post_process(size_t samples)
319             {
320                 if ((pMidi != NULL) && (pDataBuffer != NULL) && IS_OUT_PORT(pMetadata))
321                 {
322                     // Reset buffer
323                     jack_midi_clear_buffer(pDataBuffer);
324 
325                     // Transfer MIDI events
326                     pMidi->sort();  // All events SHOULD be ordered chonologically
327 
328                     // Transmit all events
329                     for (size_t i=0, events=pMidi->nEvents; i<events; ++i)
330                     {
331                         // Determine size of the message
332                         midi::event_t *ev   = &pMidi->vEvents[i];
333                         ssize_t size        = midi::size_of(ev);
334                         lsp_trace("Output event: type=0x%02x, timestamp=%d", int(ev->type), int(ev->timestamp));
335 
336                         if (size <= 0)
337                         {
338                             lsp_warn("Could not encode output MIDI message of type 0x%02x, timestamp=%d", int(ev->type), int(ev->timestamp));
339                             continue;
340                         }
341 
342                         // Allocate MIDI event
343                         jack_midi_data_t *midi_data     = jack_midi_event_reserve(pDataBuffer, ev->timestamp, size);
344                         if (midi_data == NULL)
345                         {
346                             lsp_warn("Could not write MIDI message of type 0x%02x, size=%d, timestamp=%d to JACK output port buffer=%p",
347                                     int(ev->type), int(size), int(ev->timestamp), pBuffer);
348                             continue;
349                         }
350 
351                         // Encode MIDI event
352                         midi::encode(midi_data, ev);
353                         lsp_dumpb("out midi event", midi_data, size);
354                     }
355 
356                     // Cleanup the output buffer
357                     pMidi->clear();
358                 }
359 
360                 pBuffer     = NULL;
361             }
362     };
363 
364     class JACKControlPort: public JACKPort
365     {
366         private:
367             float       fNewValue;
368             float       fCurrValue;
369 
370         public:
JACKControlPort(const port_t * meta,JACKWrapper * w)371             explicit JACKControlPort(const port_t *meta, JACKWrapper *w) : JACKPort(meta, w)
372             {
373                 fNewValue   = meta->start;
374                 fCurrValue  = meta->start;
375             }
376 
~JACKControlPort()377             virtual ~JACKControlPort()
378             {
379                 fNewValue   = pMetadata->start;
380                 fCurrValue  = pMetadata->start;
381             };
382 
383         public:
pre_process(size_t samples)384             virtual bool pre_process(size_t samples)
385             {
386                 if (fNewValue == fCurrValue)
387                     return false;
388 
389                 fCurrValue   = fNewValue;
390                 return true;
391             }
392 
getValue()393             virtual float getValue()
394             {
395                 return fCurrValue;
396             }
397 
updateValue(float value)398             void updateValue(float value)
399             {
400                 fNewValue   = limit_value(pMetadata, value);
401             }
402     };
403 
404     class JACKMeterPort: public JACKPort
405     {
406         private:
407             float       fValue;
408             bool        bForce;
409 
410         public:
JACKMeterPort(const port_t * meta,JACKWrapper * w)411             explicit JACKMeterPort(const port_t *meta, JACKWrapper *w) : JACKPort(meta, w)
412             {
413                 fValue      = meta->start;
414                 bForce      = true;
415             }
416 
~JACKMeterPort()417             virtual ~JACKMeterPort()
418             {
419                 fValue      = pMetadata->start;
420             };
421 
422         public:
getValue()423             virtual float getValue()
424             {
425                 return fValue;
426             }
427 
setValue(float value)428             virtual void setValue(float value)
429             {
430                 value   = limit_value(pMetadata, value);
431 
432                 if (pMetadata->flags & F_PEAK)
433                 {
434                     if ((bForce) || (fabs(fValue) < fabs(value)))
435                     {
436                         fValue  = value;
437                         bForce  = false;
438                     }
439                 }
440                 else
441                     fValue = value;
442             }
443 
syncValue()444             float syncValue()
445             {
446                 float value = fValue;
447                 bForce  = true;
448                 return value;
449             }
450     };
451 
452     class JACKMeshPort: public JACKPort
453     {
454         private:
455             mesh_t     *pMesh;
456 
457         public:
JACKMeshPort(const port_t * meta,JACKWrapper * w)458             explicit JACKMeshPort(const port_t *meta, JACKWrapper *w) : JACKPort(meta, w)
459             {
460                 pMesh   = NULL;
461             }
462 
~JACKMeshPort()463             virtual ~JACKMeshPort()
464             {
465                 pMesh   = NULL;
466             }
467 
468         public:
getBuffer()469             virtual void *getBuffer()
470             {
471                 return pMesh;
472             }
473 
init()474             virtual int init()
475             {
476                 pMesh   = jack_create_mesh(pMetadata);
477                 return (pMesh == NULL) ? STATUS_NO_MEM : STATUS_OK;
478             }
479 
destroy()480             virtual void destroy()
481             {
482                 if (pMesh != NULL)
483                 {
484                     jack_destroy_mesh(pMesh);
485                     pMesh = NULL;
486                 }
487             }
488     };
489 
490     class JACKStreamPort: public JACKPort
491     {
492         private:
493             stream_t       *pStream;
494 
495         public:
JACKStreamPort(const port_t * meta,JACKWrapper * w)496             explicit JACKStreamPort(const port_t *meta, JACKWrapper *w): JACKPort(meta, w)
497             {
498                 pStream     = NULL;
499             }
500 
~JACKStreamPort()501             virtual ~JACKStreamPort()
502             {
503                 pStream     = NULL;
504             }
505 
506         public:
getBuffer()507             virtual void *getBuffer()
508             {
509                 return pStream;
510             }
511 
init()512             virtual int init()
513             {
514                 pStream = stream_t::create(pMetadata->min, pMetadata->max, pMetadata->start);
515                 return (pStream == NULL) ? STATUS_NO_MEM : STATUS_OK;
516             }
517 
destroy()518             virtual void destroy()
519             {
520                 stream_t::destroy(pStream);
521                 pStream     = NULL;
522             }
523     };
524 
525     class JACKFrameBufferPort: public JACKPort
526     {
527         private:
528             frame_buffer_t      sFB;
529 
530         public:
JACKFrameBufferPort(const port_t * meta,JACKWrapper * w)531             explicit JACKFrameBufferPort(const port_t *meta, JACKWrapper *w) : JACKPort(meta, w)
532             {
533             }
534 
~JACKFrameBufferPort()535             virtual ~JACKFrameBufferPort()
536             {
537             }
538 
539         public:
getBuffer()540             virtual void *getBuffer()
541             {
542                 return &sFB;
543             }
544 
init()545             virtual int init()
546             {
547                 return sFB.init(pMetadata->start, pMetadata->step);
548             }
549 
destroy()550             virtual void destroy()
551             {
552                 sFB.destroy();
553             }
554     };
555 
556     class JACKOscPort: public JACKPort
557     {
558         private:
559             osc_buffer_t     *pFB;
560 
561         public:
JACKOscPort(const port_t * meta,JACKWrapper * w)562             explicit JACKOscPort(const port_t *meta, JACKWrapper *w) : JACKPort(meta, w)
563             {
564                 pFB     = NULL;
565             }
566 
~JACKOscPort()567             virtual ~JACKOscPort()
568             {
569             }
570 
571         public:
getBuffer()572             virtual void *getBuffer()
573             {
574                 return pFB;
575             }
576 
init()577             virtual int init()
578             {
579                 pFB = osc_buffer_t::create(OSC_BUFFER_MAX);
580                 return (pFB == NULL) ? STATUS_NO_MEM : STATUS_OK;
581             }
582 
destroy()583             virtual void destroy()
584             {
585                 if (pFB != NULL)
586                 {
587                     osc_buffer_t::destroy(pFB);
588                     pFB     = NULL;
589                 }
590             }
591     };
592 
593     class JACKPathPort: public JACKPort
594     {
595         private:
596             jack_path_t     sPath;
597 
598         public:
JACKPathPort(const port_t * meta,JACKWrapper * w)599             explicit JACKPathPort(const port_t *meta, JACKWrapper *w) : JACKPort(meta, w)
600             {
601                 sPath.init();
602             }
603 
~JACKPathPort()604             virtual ~JACKPathPort()
605             {
606             }
607 
608         public:
getBuffer()609             virtual void *getBuffer()
610             {
611                 return static_cast<path_t *>(&sPath);
612             }
613 
pre_process(size_t samples)614             virtual bool pre_process(size_t samples)
615             {
616                 return sPath.pending();
617             }
618     };
619 
620 }
621 
622 
623 #endif /* COINTAINER_JACK_PORTS_H_ */
624