1 /******************************************************************************\
2  * Copyright (c) 2004-2020
3  *
4  * Author(s):
5  *  Volker Fischer
6  *
7  ******************************************************************************
8  *
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation; either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22  *
23 \******************************************************************************/
24 
25 #pragma once
26 
27 #ifndef JACK_REPLACES_ASIO // these headers are not available in Windows OS
28 #    include <unistd.h>
29 #    include <sys/ioctl.h>
30 #endif
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <QThread>
36 #include <string.h>
37 #include "util.h"
38 #include "soundbase.h"
39 #include "global.h"
40 
41 #if WITH_JACK
42 #    include <jack/jack.h>
43 #    include <jack/midiport.h>
44 #endif
45 
46 /* Definitions ****************************************************************/
47 #define NUM_IN_OUT_CHANNELS 2 // always stereo
48 
49 // the number of periods is critical for latency
50 #define NUM_PERIOD_BLOCKS_IN  2
51 #define NUM_PERIOD_BLOCKS_OUT 1
52 
53 #define MAX_SND_BUF_IN  200
54 #define MAX_SND_BUF_OUT 200
55 
56 /* Classes ********************************************************************/
57 #if WITH_JACK
58 class CSound : public CSoundBase
59 {
60     Q_OBJECT
61 
62 public:
CSound(void (* fpNewProcessCallback)(CVector<short> & psData,void * arg),void * arg,const QString & strMIDISetup,const bool bNoAutoJackConnect,const QString & strJackClientName)63     CSound ( void ( *fpNewProcessCallback ) ( CVector<short>& psData, void* arg ),
64              void*          arg,
65              const QString& strMIDISetup,
66              const bool     bNoAutoJackConnect,
67              const QString& strJackClientName ) :
68         CSoundBase ( "Jack", fpNewProcessCallback, arg, strMIDISetup ),
69         iJACKBufferSizeMono ( 0 ),
70         bJackWasShutDown ( false ),
71         fInOutLatencyMs ( 0.0f )
72     {
73         QString strJackName = QString ( APP_NAME );
74 
75         if ( !strJackClientName.isEmpty() )
76         {
77             strJackName += " " + strJackClientName;
78         }
79 
80         OpenJack ( bNoAutoJackConnect, strJackName.toLocal8Bit().data() );
81     }
82 
~CSound()83     virtual ~CSound() { CloseJack(); }
84 
85     virtual int  Init ( const int iNewPrefMonoBufferSize );
86     virtual void Start();
87     virtual void Stop();
88 
GetInOutLatencyMs()89     virtual float GetInOutLatencyMs() { return fInOutLatencyMs; }
90 
91     // these variables should be protected but cannot since we want
92     // to access them from the callback function
93     CVector<short> vecsTmpAudioSndCrdStereo;
94     int            iJACKBufferSizeMono;
95     int            iJACKBufferSizeStero;
96     bool           bJackWasShutDown;
97 
98     jack_port_t* input_port_left;
99     jack_port_t* input_port_right;
100     jack_port_t* output_port_left;
101     jack_port_t* output_port_right;
102     jack_port_t* input_port_midi;
103 
104 protected:
105     void OpenJack ( const bool bNoAutoJackConnect, const char* jackClientName );
106 
107     void CloseJack();
108 
109     // callbacks
110     static int     process ( jack_nframes_t nframes, void* arg );
111     static int     bufferSizeCallback ( jack_nframes_t, void* arg );
112     static void    shutdownCallback ( void* );
113     jack_client_t* pJackClient;
114 
115     float fInOutLatencyMs;
116 };
117 #else
118 // no sound -> dummy class definition
119 #    include "server.h"
120 class CSound : public CSoundBase
121 {
122     Q_OBJECT
123 
124 public:
CSound(void (* fpNewProcessCallback)(CVector<short> & psData,void * pParg),void * pParg,const QString & strMIDISetup,const bool,const QString &)125     CSound ( void ( *fpNewProcessCallback ) ( CVector<short>& psData, void* pParg ),
126              void*          pParg,
127              const QString& strMIDISetup,
128              const bool,
129              const QString& ) :
130         CSoundBase ( "nosound", fpNewProcessCallback, pParg, strMIDISetup ),
131         HighPrecisionTimer ( true )
132     {
133         HighPrecisionTimer.Start();
134         QObject::connect ( &HighPrecisionTimer, &CHighPrecisionTimer::timeout, this, &CSound::OnTimer );
135     }
~CSound()136     virtual ~CSound() {}
Init(const int iNewPrefMonoBufferSize)137     virtual int Init ( const int iNewPrefMonoBufferSize )
138     {
139         CSoundBase::Init ( iNewPrefMonoBufferSize );
140         vecsTemp.Init ( 2 * iNewPrefMonoBufferSize );
141         return iNewPrefMonoBufferSize;
142     }
143     CHighPrecisionTimer HighPrecisionTimer;
144     CVector<short>      vecsTemp;
145 
146 public slots:
OnTimer()147     void OnTimer()
148     {
149         vecsTemp.Reset ( 0 );
150         if ( IsRunning() )
151         {
152             ProcessCallback ( vecsTemp );
153         }
154     }
155 };
156 #endif // WITH_JACK
157