1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2009 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #define RG_MODULE_STRING "[JackCaptureClient]"
19 
20 #include "JackCaptureClient.h"
21 #include "misc/Debug.h"
22 
23 #define DEBUG_JACK_CAPTURE_CLIENT 0
24 
25 namespace Rosegarden
26 {
27 
28 
29 /*************************************
30   CONSTRUCTOR, DESTRUCTOR, AND INIT
31  *************************************/
32 JackCaptureClient::JackCaptureClient( const char *captureClientName, int fs ) :
33         m_isConnected(false),
34         m_processing(false),
35         m_jackRingBuffer(nullptr),
36         m_frameSize(fs)
37 {
38     // Try to connect to Jack server
39     //if ( (client = jack_client_new(captureClientName)) == 0 ) {
40     if ( (client = jack_client_open(captureClientName, JackNullOption, nullptr)) == nullptr ) {
41         return;
42     }
43 #if DEBUG_JACK_CAPTURE_CLIENT
44     RG_DEBUG << "Registered as Jack client";
45 #endif
46 
47     // get stream info
48     m_jackSampleRate = jack_get_sample_rate( client );
49     m_jackBufferSize = jack_get_buffer_size( client );
50     m_jackSampleSize = sizeof(jack_default_audio_sample_t);
51 #if DEBUG_JACK_CAPTURE_CLIENT
52     RG_DEBUG << "Sample Rate " << m_jackSampleRate;
53     RG_DEBUG << "Max buffer size " << m_jackBufferSize;
54     RG_DEBUG << "Sample size (bytes) " << m_jackSampleSize;
55 #endif
56 
57     //setup ringbuffer
58     setFrameSize(m_frameSize);
59 
60     //register process and shutdown calls
61     jack_set_process_callback(client, &process, (void*)this);
62     jack_on_shutdown(client, jackShutdown, (void*)this);
63 #if DEBUG_JACK_CAPTURE_CLIENT
64     RG_DEBUG << "Process and shutdown calls registered";
65 #endif
66 
67     // Activate
68     if ( jack_activate(client) ) {
69         RG_WARNING << "Can't activate client";
70         throw("Cannot activate client");
71     }
72 #if DEBUG_JACK_CAPTURE_CLIENT
73     RG_DEBUG << "Activated";
74 #endif
75 
76     //set default port to the first available port
77     const char **ports = getPorts();
78     setupPorts(ports[0], captureClientName);
79 
80     m_isConnected = true;
81 }
82 
83 JackCaptureClient::~JackCaptureClient()
84 {
85     stopProcessing();
86     jack_client_close(client);
87     if (m_jackRingBuffer) jack_ringbuffer_free(m_jackRingBuffer);
88 }
89 
90 void
91 JackCaptureClient::setFrameSize(int nextFrameSize)
92 {
93     m_frameSize = nextFrameSize + 1;
94 
95 #if DEBUG_JACK_CAPTURE_CLIENT
96     RG_DEBUG << "CaptureClient: setting framesize to at least"
97               << m_frameSize;
98 #endif
99 
100     if (m_processing) {
101         RG_DEBUG << "CaptureClient: Procesing, can't change framesize";
102         return;
103     }
104 
105     if (m_jackRingBuffer) jack_ringbuffer_free(m_jackRingBuffer);
106 
107     // framesize must be larger than size of max buffer size (m_jackBufferSize)
108     // else a complete jack frame can never be written to ringbuffer
109     if ( m_frameSize < m_jackBufferSize ) m_frameSize = m_jackBufferSize+1;
110 
111     size_t m_jackRingBufferSize = m_jackSampleSize * m_frameSize;
112     m_jackRingBuffer = jack_ringbuffer_create( m_jackRingBufferSize );
113     jack_ringbuffer_reset( m_jackRingBuffer );
114 #if DEBUG_JACK_CAPTURE_CLIENT
115     RG_DEBUG << "Created ringbuffer, write space: "
116               << jack_ringbuffer_write_space(m_jackRingBuffer)
117               << "read space: "
118               << jack_ringbuffer_read_space(m_jackRingBuffer)
119               << " m_jackSampleSize " << m_jackSampleSize;
120 #endif
121 }
122 
123 const char
124 **JackCaptureClient::getPorts()
125 {
126     return jack_get_ports( client, nullptr, nullptr, JackPortIsOutput );
127 }
128 
129 const char*
130 JackCaptureClient::getCapturePortName()
131 {
132     return jack_port_name( m_capturePort);
133 }
134 
135 void
136 JackCaptureClient::setupPorts(const char *portName,
137                               const char *captureClientName)
138 {
139 #if DEBUG_JACK_CAPTURE_CLIENT
140     RG_DEBUG << "Connecting ports...";
141 #endif
142 
143     // register port
144     std::string inPortName = captureClientName;
145     inPortName.append(" In");
146 
147 #if DEBUG_JACK_CAPTURE_CLIENT
148     RG_DEBUG << "Registering input port as:" << inPortName;
149 #endif
150     inPort = jack_port_register(client, inPortName.c_str(),
151                                 JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
152     if (inPort == nullptr) {
153         RG_DEBUG << "Cannot open Jack port";
154     }
155 
156     if ( jack_port_connected(inPort) ) {
157 #if DEBUG_JACK_CAPTURE_CLIENT
158         RG_DEBUG << "Disconnecting ports";
159 #endif
160 
161         const char **connectedPorts = jack_port_get_connections(inPort);
162         int i=0;
163         while (connectedPorts[i] != nullptr)
164         {
165 #if DEBUG_JACK_CAPTURE_CLIENT
166             RG_DEBUG << "disconnecting from " << connectedPorts[i];
167 #endif
168             jack_port_disconnect(client, inPort);
169             i++;
170         }
171     }
172 
173     m_capturePort = jack_port_by_name(client, portName);
174 
175 #if DEBUG_JACK_CAPTURE_CLIENT
176     RG_DEBUG << "Recording from" << jack_port_name(m_capturePort);
177 #endif
178 
179     // Connect ports
180     if (jack_connect( client, portName, jack_port_name(inPort) ) < 0 )
181     {
182         RG_DEBUG << "------------------------------"
183                   << "Jack Client: cant connect port"
184                   << "------------------------------";
185     }
186 
187 #if DEBUG_JACK_CAPTURE_CLIENT
188     RG_DEBUG << "Port connected";
189 #endif
190 }
191 
192 
193 /*********************************
194         REALTIME OPERATIONS
195  ********************************/
196 int
197 JackCaptureClient::process(jack_nframes_t nframes, void *arg)
198 {
199     JackCaptureClient *jcc = (JackCaptureClient*)arg;
200     if ( !jcc->m_processing ) {
201         return 0;
202     }
203 
204     jack_default_audio_sample_t *inSamp = (jack_default_audio_sample_t *)
205                                           jack_port_get_buffer(
206                                               jcc->m_capturePort,
207                                               nframes);
208 
209     //check space to write
210     uint writeSpace = jack_ringbuffer_write_space( jcc->m_jackRingBuffer );
211     int writeSamples = writeSpace / jcc->m_jackSampleSize;
212 
213 #if DEBUG_JACK_CAPTURE_CLIENT
214     RG_DEBUG << "Want to write" << nframes << "frames\t"
215               << writeSamples << "available";
216 #endif
217 
218 
219     if ( writeSpace < (jcc->m_jackSampleSize * nframes) ) {
220         unsigned int advance = nframes - writeSamples;
221         unsigned int advanceBytes = advance * jcc->m_jackSampleSize;
222         jack_ringbuffer_read_advance( jcc->m_jackRingBuffer, advanceBytes );
223 #if DEBUG_JACK_CAPTURE_CLIENT
224         RG_DEBUG << "Advancing read pointer" << advance << "frames,"
225                   << advanceBytes << "bytes";
226         writeSpace = jack_ringbuffer_write_space( jcc->m_jackRingBuffer );
227         writeSamples = writeSpace / jcc->m_jackSampleSize;
228         RG_DEBUG << writeSamples << "frames now available";
229 #endif
230     }
231 
232 
233     size_t written = jack_ringbuffer_write(jcc->m_jackRingBuffer,
234                                            (char*)(inSamp),
235                                            jcc->m_jackSampleSize * nframes);
236 #if DEBUG_JACK_CAPTURE_CLIENT
237     RG_DEBUG << "I've written" << written / jcc->m_jackSampleSize << "frames";
238 #else
239     (void) written; // stops warning about unused variable
240 #endif
241 
242 #if DEBUG_JACK_CAPTURE_CLIENT
243     uint readSpace = jack_ringbuffer_read_space( jcc->m_jackRingBuffer );
244     uint readSamples = readSpace / jcc->m_jackSampleSize;
245     RG_DEBUG << "Now" << readSamples << "samples available to read";
246 #endif
247 
248     return 0;
249 }
250 
251 
252 /*********************************
253     CONTROL REALTIME OPERATIONS
254  ********************************/
255 void
256 JackCaptureClient::startProcessing()
257 {
258     if (m_isConnected) {
259         m_processing = true;
260     }
261 }
262 
263 void
264 JackCaptureClient::stopProcessing()
265 {
266     m_processing = false;
267 }
268 
269 // Method will be called if Jack shuts process down
270 void
271 JackCaptureClient::jackShutdown(void *arg)
272 {
273     (void)arg;
274 #if DEBUG_JACK_CAPTURE_CLIENT
275 //    JackCaptureClient *jcc = (JackCaptureClient*)arg;
276     RG_DEBUG << "Shutdown by Jack!!!!!!!!!";
277 #endif
278 }
279 
280 
281 /*********************************
282           GET SAMPLE DATA
283  ********************************/
284 bool
285 JackCaptureClient::getFrame(float *frame, size_t captureSize)
286 {
287     size_t availableSize = jack_ringbuffer_read_space(m_jackRingBuffer)
288                            / m_jackSampleSize;
289     // ensure a full chunk is available & processing is allowed
290     if (captureSize <= availableSize)
291     {
292         size_t read = jack_ringbuffer_read(m_jackRingBuffer,
293                                            (char*)(frame),
294                                            (sizeof(float)*captureSize) );
295         (void) read; // stops warning about unused variable
296 #if DEBUG_JACK_CAPTURE_CLIENT
297         RG_DEBUG << "JackCaptureClient::getFrame - Got frame!";
298 #endif
299         return true;
300     }
301     else {
302 #if DEBUG_JACK_CAPTURE_CLIENT
303         RG_DEBUG << "JackCaptureClient::getFrame - "
304                   << availableSize << " samples available. "
305                   << captureSize << " samples wanted";
306 #endif
307         return false;
308     }
309 }
310 
311 
312 } // end namespace
313 
314