Chris@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 Sonic Visualiser
|
Chris@0
|
5 An audio file viewer and annotation editor.
|
Chris@0
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@0
|
7 This file copyright 2006 Chris Cannam.
|
Chris@0
|
8
|
Chris@0
|
9 This program is free software; you can redistribute it and/or
|
Chris@0
|
10 modify it under the terms of the GNU General Public License as
|
Chris@0
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@0
|
12 License, or (at your option) any later version. See the file
|
Chris@0
|
13 COPYING included with this distribution for more information.
|
Chris@0
|
14 */
|
Chris@0
|
15
|
Chris@0
|
16 #ifdef HAVE_JACK
|
Chris@0
|
17
|
Chris@0
|
18 #include "AudioJACKTarget.h"
|
Chris@0
|
19 #include "AudioCallbackPlaySource.h"
|
Chris@0
|
20
|
Chris@0
|
21 #include <iostream>
|
Chris@0
|
22 #include <cmath>
|
Chris@0
|
23
|
Chris@0
|
24 //#define DEBUG_AUDIO_JACK_TARGET 1
|
Chris@0
|
25
|
Chris@0
|
26 AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
|
Chris@0
|
27 AudioCallbackPlayTarget(source),
|
Chris@0
|
28 m_client(0),
|
Chris@0
|
29 m_bufferSize(0),
|
Chris@0
|
30 m_sampleRate(0)
|
Chris@0
|
31 {
|
Chris@0
|
32 char name[20];
|
Chris@0
|
33 strcpy(name, "Sonic Visualiser");
|
Chris@0
|
34 m_client = jack_client_new(name);
|
Chris@0
|
35
|
Chris@0
|
36 if (!m_client) {
|
Chris@0
|
37 sprintf(name, "Sonic Visualiser (%d)", (int)getpid());
|
Chris@0
|
38 m_client = jack_client_new(name);
|
Chris@0
|
39 if (!m_client) {
|
Chris@0
|
40 std::cerr
|
Chris@0
|
41 << "ERROR: AudioJACKTarget: Failed to connect to JACK server"
|
Chris@0
|
42 << std::endl;
|
Chris@0
|
43 }
|
Chris@0
|
44 }
|
Chris@0
|
45
|
Chris@0
|
46 if (!m_client) return;
|
Chris@0
|
47
|
Chris@0
|
48 m_bufferSize = jack_get_buffer_size(m_client);
|
Chris@0
|
49 m_sampleRate = jack_get_sample_rate(m_client);
|
Chris@0
|
50
|
Chris@0
|
51 jack_set_process_callback(m_client, processStatic, this);
|
Chris@0
|
52
|
Chris@0
|
53 if (jack_activate(m_client)) {
|
Chris@0
|
54 std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
|
Chris@0
|
55 << std::endl;
|
Chris@0
|
56 }
|
Chris@0
|
57
|
Chris@0
|
58 if (m_source) {
|
Chris@0
|
59 sourceModelReplaced();
|
Chris@0
|
60 }
|
Chris@0
|
61 }
|
Chris@0
|
62
|
Chris@0
|
63 AudioJACKTarget::~AudioJACKTarget()
|
Chris@0
|
64 {
|
Chris@0
|
65 if (m_client) {
|
Chris@0
|
66 jack_deactivate(m_client);
|
Chris@0
|
67 jack_client_close(m_client);
|
Chris@0
|
68 }
|
Chris@0
|
69 }
|
Chris@0
|
70
|
Chris@0
|
71 bool
|
Chris@0
|
72 AudioJACKTarget::isOK() const
|
Chris@0
|
73 {
|
Chris@0
|
74 return (m_client != 0);
|
Chris@0
|
75 }
|
Chris@0
|
76
|
Chris@0
|
77 int
|
Chris@0
|
78 AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
|
Chris@0
|
79 {
|
Chris@0
|
80 return ((AudioJACKTarget *)arg)->process(nframes);
|
Chris@0
|
81 }
|
Chris@0
|
82
|
Chris@0
|
83 void
|
Chris@0
|
84 AudioJACKTarget::sourceModelReplaced()
|
Chris@0
|
85 {
|
Chris@0
|
86 m_mutex.lock();
|
Chris@0
|
87
|
Chris@0
|
88 m_source->setTargetBlockSize(m_bufferSize);
|
Chris@0
|
89 m_source->setTargetSampleRate(m_sampleRate);
|
Chris@0
|
90
|
Chris@0
|
91 size_t channels = m_source->getSourceChannelCount();
|
Chris@0
|
92
|
Chris@0
|
93 // Because we offer pan, we always want at least 2 channels
|
Chris@0
|
94 if (channels < 2) channels = 2;
|
Chris@0
|
95
|
Chris@0
|
96 if (channels == m_outputs.size() || !m_client) {
|
Chris@0
|
97 m_mutex.unlock();
|
Chris@0
|
98 return;
|
Chris@0
|
99 }
|
Chris@0
|
100
|
Chris@0
|
101 const char **ports =
|
Chris@0
|
102 jack_get_ports(m_client, NULL, NULL,
|
Chris@0
|
103 JackPortIsPhysical | JackPortIsInput);
|
Chris@0
|
104 size_t physicalPortCount = 0;
|
Chris@0
|
105 while (ports[physicalPortCount]) ++physicalPortCount;
|
Chris@0
|
106
|
Chris@0
|
107 #ifdef DEBUG_AUDIO_JACK_TARGET
|
Chris@0
|
108 std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl;
|
Chris@0
|
109 #endif
|
Chris@0
|
110
|
Chris@0
|
111 while (m_outputs.size() < channels) {
|
Chris@0
|
112
|
Chris@0
|
113 char name[20];
|
Chris@0
|
114 jack_port_t *port;
|
Chris@0
|
115
|
Chris@0
|
116 sprintf(name, "out %d", m_outputs.size() + 1);
|
Chris@0
|
117
|
Chris@0
|
118 port = jack_port_register(m_client,
|
Chris@0
|
119 name,
|
Chris@0
|
120 JACK_DEFAULT_AUDIO_TYPE,
|
Chris@0
|
121 JackPortIsOutput,
|
Chris@0
|
122 0);
|
Chris@0
|
123
|
Chris@0
|
124 if (!port) {
|
Chris@0
|
125 std::cerr
|
Chris@0
|
126 << "ERROR: AudioJACKTarget: Failed to create JACK output port "
|
Chris@0
|
127 << m_outputs.size() << std::endl;
|
Chris@0
|
128 } else {
|
Chris@0
|
129 m_source->setTargetPlayLatency(jack_port_get_latency(port));
|
Chris@0
|
130 }
|
Chris@0
|
131
|
Chris@0
|
132 if (m_outputs.size() < physicalPortCount) {
|
Chris@0
|
133 jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
|
Chris@0
|
134 }
|
Chris@0
|
135
|
Chris@0
|
136 m_outputs.push_back(port);
|
Chris@0
|
137 }
|
Chris@0
|
138
|
Chris@0
|
139 while (m_outputs.size() > channels) {
|
Chris@0
|
140 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
|
Chris@0
|
141 --itr;
|
Chris@0
|
142 jack_port_t *port = *itr;
|
Chris@0
|
143 if (port) jack_port_unregister(m_client, port);
|
Chris@0
|
144 m_outputs.erase(itr);
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 m_mutex.unlock();
|
Chris@0
|
148 }
|
Chris@0
|
149
|
Chris@0
|
150 int
|
Chris@0
|
151 AudioJACKTarget::process(jack_nframes_t nframes)
|
Chris@0
|
152 {
|
Chris@0
|
153 if (!m_mutex.tryLock()) {
|
Chris@0
|
154 return 0;
|
Chris@0
|
155 }
|
Chris@0
|
156
|
Chris@0
|
157 if (m_outputs.empty()) {
|
Chris@0
|
158 m_mutex.unlock();
|
Chris@0
|
159 return 0;
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@0
|
162 #ifdef DEBUG_AUDIO_JACK_TARGET
|
Chris@0
|
163 std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl;
|
Chris@0
|
164 #endif
|
Chris@0
|
165
|
Chris@0
|
166 #ifdef DEBUG_AUDIO_JACK_TARGET
|
Chris@0
|
167 if (m_bufferSize != nframes) {
|
Chris@0
|
168 std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl;
|
Chris@0
|
169 }
|
Chris@0
|
170 #endif
|
Chris@0
|
171
|
Chris@0
|
172 float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
|
Chris@0
|
173
|
Chris@0
|
174 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
|
Chris@0
|
175 buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
|
Chris@0
|
176 }
|
Chris@0
|
177
|
Chris@0
|
178 if (m_source) {
|
Chris@0
|
179 m_source->getSourceSamples(nframes, buffers);
|
Chris@0
|
180 } else {
|
Chris@0
|
181 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
|
Chris@0
|
182 for (size_t i = 0; i < nframes; ++i) {
|
Chris@0
|
183 buffers[ch][i] = 0.0;
|
Chris@0
|
184 }
|
Chris@0
|
185 }
|
Chris@0
|
186 }
|
Chris@0
|
187
|
Chris@0
|
188 float peakLeft = 0.0, peakRight = 0.0;
|
Chris@0
|
189
|
Chris@0
|
190 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
|
Chris@0
|
191
|
Chris@0
|
192 float peak = 0.0;
|
Chris@0
|
193
|
Chris@0
|
194 for (size_t i = 0; i < nframes; ++i) {
|
Chris@0
|
195 buffers[ch][i] *= m_outputGain;
|
Chris@0
|
196 float sample = fabsf(buffers[ch][i]);
|
Chris@0
|
197 if (sample > peak) peak = sample;
|
Chris@0
|
198 }
|
Chris@0
|
199
|
Chris@0
|
200 if (ch == 0) peakLeft = peak;
|
Chris@0
|
201 if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
|
Chris@0
|
202 }
|
Chris@0
|
203
|
Chris@0
|
204 if (m_source) {
|
Chris@0
|
205 m_source->setOutputLevels(peakLeft, peakRight);
|
Chris@0
|
206 }
|
Chris@0
|
207
|
Chris@0
|
208 m_mutex.unlock();
|
Chris@0
|
209 return 0;
|
Chris@0
|
210 }
|
Chris@0
|
211
|
Chris@0
|
212
|
Chris@0
|
213 #ifdef INCLUDE_MOCFILES
|
Chris@0
|
214 #include "AudioJACKTarget.moc.cpp"
|
Chris@0
|
215 #endif
|
Chris@0
|
216
|
Chris@0
|
217 #endif /* HAVE_JACK */
|
Chris@0
|
218
|