MythTV  master
audiooutputjack.cpp
Go to the documentation of this file.
1 /*
2  * JACK AudioOutput module
3  * Written by Ed Wildgoose in 2010 with improvements from various authors
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <cerrno>
21 #include <cmath>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25 #include <ctime>
26 #include <fcntl.h>
27 #include <sys/time.h>
28 #include <unistd.h>
29 
31 #include "libmythbase/mythdate.h"
32 
33 #include "audiooutputjack.h"
34 
35 #define LOC QString("AOJack: ")
36 
38  AudioOutputBase(settings)
39 {
40  m_ports.fill( nullptr);
41  m_chanVolumes.fill(100);
42 
43  // Set everything up
44  InitSettings(settings);
45  if (settings.m_init)
46  Reconfigure(settings);
47 }
48 
50 {
51  int rate = 0;
52  int i = 0;
53  const char **matching_ports = nullptr;
54  auto *settings = new AudioOutputSettings();
55 
57  if (!m_client)
58  {
59  Error(LOC + tr("Cannot start/connect to jack server "
60  "(to check supported rate/channels)"));
61  delete settings;
62  return nullptr;
63  }
64 
65  if (m_client)
66  rate = jack_get_sample_rate(m_client);
67 
68  if (!rate)
69  {
70  Error(LOC + tr("Unable to retrieve jack server sample rate"));
71  goto err_out;
72  }
73  else
74  settings->AddSupportedRate(rate);
75 
76  // Jack only wants float format samples (de-interleaved for preference)
77  settings->AddSupportedFormat(FORMAT_FLT);
78 
79  // Find some Jack ports to connect to
80  matching_ports = JackGetPorts();
81 
82  if (!matching_ports || !matching_ports[0])
83  {
84  Error(LOC + tr("No ports available to connect to"));
85  goto err_out;
86  }
87  // Count matching ports from 2nd port upwards
88  i = 1;
89  while ((i < JACK_CHANNELS_MAX) && matching_ports[i])
90  {
91  settings->AddSupportedChannels(i+1);
92  VBAUDIO(QString("Adding channels: %1").arg(i+1));
93  i++;
94  }
95 
96  // Currently this looks very similar to error code - duplicated for safety
97  free(matching_ports);
99  return settings;
100 
101 err_out:
102  // Our abstracted exit point in case of error
103  free(matching_ports);
105  delete settings;
106  return nullptr;
107 }
108 
109 
111 {
112  // Close down all audio stuff
113  KillAudio();
114 }
115 
117 {
118  const char **matching_ports = nullptr;
119  int i = 0;
120 
121  // We have a hard coded channel limit - check we haven't exceeded it
123  {
124  Error(LOC + tr("Requested more channels: (%1), than the maximum: %2")
125  .arg(m_channels).arg(JACK_CHANNELS_MAX));
126  return false;
127  }
128 
129  VBAUDIO( QString("Opening JACK audio device: '%1'.")
130  .arg(m_mainDevice));
131 
132  // Setup volume control
133  if (m_internalVol)
134  VolumeInit();
135 
136  // Connect to the Jack audio server
138  if (!m_client)
139  {
140  Error(LOC + tr("Cannot start/connect to jack server"));
141  goto err_out;
142  }
143 
144  // Find some Jack ports to connect to
145  matching_ports = JackGetPorts();
146  if (!matching_ports || !matching_ports[0])
147  {
148  Error(LOC + tr("No ports available to connect to"));
149  goto err_out;
150  }
151 
152  // Count matching ports
153  i = 1;
154  while (matching_ports[i])
155  i++;
156  // ensure enough ports to satisfy request
157  if (m_channels > i)
158  {
159  Error(LOC + tr("Not enough ports available to connect to"));
160  goto err_out;
161  }
162 
163  // Create our output ports
164  for (i = 0; i < m_channels; i++)
165  {
166  QString port_name = QString("out_%1").arg(i);
167  m_ports[i] = jack_port_register(m_client, port_name.toLatin1().constData(),
168  JACK_DEFAULT_AUDIO_TYPE,
169  JackPortIsOutput, 0);
170  if (!m_ports[i])
171  {
172  Error(LOC + tr("Error while registering new jack port: %1").arg(i));
173  goto err_out;
174  }
175  }
176 
177  // Note some basic soundserver parameters
178  m_sampleRate = jack_get_sample_rate(m_client);
179 
180  // Get the size of our callback buffer in bytes
181  m_fragmentSize = jack_get_buffer_size(m_client) * m_outputBytesPerFrame;
182 
183  // Allocate a new temp buffer to de-interleave our audio data
184  delete[] m_auBuf;
185  m_auBuf = new unsigned char[m_fragmentSize];
186 
187  // Set our callbacks
188  // These will actually get called after jack_activate()!
189  // ...Possibly even before this OpenDevice sub returns...
190  if (jack_set_process_callback(m_client, JackCallbackHelper, this))
191  Error(LOC + tr("Error. Unable to set process callback?!"));
192  if (jack_set_xrun_callback(m_client, JackXRunCallbackHelper, this))
193  Error(LOC + tr("Error. Unable to set xrun callback?!"));
194  if (jack_set_graph_order_callback(m_client, JackGraphOrderCallbackHelper, this))
195  Error(LOC + tr("Error. Unable to set graph order change callback?!"));
196 
197  // Activate! Everything comes into life after here. Beware races
198  if (jack_activate(m_client))
199  {
200  Error(LOC + tr("Calling jack_activate failed"));
201  goto err_out;
202  }
203 
204  // Connect our output ports
205  if (! JackConnectPorts(matching_ports))
206  goto err_out;
207 
208  // Free up some stuff
209  free(matching_ports);
210 
211  // Device opened successfully
212  return true;
213 
214 err_out:
215  // Our abstracted exit point in case of error
216  free(matching_ports);
218  return false;
219 }
220 
222 {
225  if (m_auBuf)
226  {
227  delete[] m_auBuf;
228  m_auBuf = nullptr;
229  }
230 
231  VBAUDIO("Jack: Stop Event");
233  dispatch(e);
234 }
235 
236 
238 {
239  int frames_played = jack_frames_since_cycle_start (this->m_client);
240  LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
241  QString("Stats: frames_since_cycle_start:%1 fragment_size:%2")
242  .arg(frames_played).arg(m_fragmentSize));
243  return (m_fragmentSize * 2) - (frames_played * m_outputBytesPerFrame);
244 }
245 
246 /* Converts buffer to jack buffers
247  Input: aubuf: interleaved buffer of currently assumed to be 32bit floats
248  nframes: number of frames of output required
249  Output: bufs: non interleaved float values.
250 */
251 void AudioOutputJACK::DeinterleaveAudio(const float *aubuf, float **bufs, int nframes,
252  const jack_vol_array& channel_volumes)
253 {
254  // Convert to floats and de-interleave
255  // TODO: Implicit assumption dealing with float input only.
256  short sample = 0;
257 
258  // Create a local float version of the channel_volumes array
259  // TODO: This can probably be removed
260  // if we have float software volume control in AOB?
261  std::array<float,JACK_CHANNELS_MAX> volumes {};
262  for (int channel = 0; channel < m_channels; channel++)
263  {
264  if (m_internalVol)
265  {
266  // Software volume control - we use an exponential adjustment
267  // (perhaps should be log?)
268  volumes[channel] = (float) (( channel_volumes[channel] *
269  channel_volumes[channel] ) /
270  10000.0);
271  }
272  else
273  volumes[channel] = 1.0 / 1.0; // ie no effect
274  }
275 
276  if (m_channels == 2)
277  {
278  for (int frame = 0; frame < nframes; frame++)
279  {
280  bufs[0][frame] = aubuf[sample++] * volumes[0];
281  bufs[1][frame] = aubuf[sample++] * volumes[1];
282  }
283  }
284  else if (m_channels == 6)
285  {
286  for (int frame = 0; frame < nframes; frame++)
287  {
288  // Audio supplied in SMPTE l,r,ce,lfe,lr,rr
289  // We probably want it in ALSA format l,r,lr,rr,ce,lfe
290  bufs[0][frame] = aubuf[sample++] * volumes[0];
291  bufs[1][frame] = aubuf[sample++] * volumes[1];
292  bufs[4][frame] = aubuf[sample++] * volumes[4];
293  bufs[5][frame] = aubuf[sample++] * volumes[5];
294  bufs[2][frame] = aubuf[sample++] * volumes[2];
295  bufs[3][frame] = aubuf[sample++] * volumes[3];
296  }
297  }
298  else if (m_channels == 8)
299  {
300  for (int frame = 0; frame < nframes; frame++)
301  {
302  // Audio supplied in SMPTE l,r,ce,lfe,lr,rr,ml,mr ?
303  // We probably want it in ALSA format l,r,lr,rr,ce,lfe,ml,mr ?
304  // TODO - unknown if this channel ordering is correct?
305  bufs[0][frame] = aubuf[sample++] * volumes[0];
306  bufs[1][frame] = aubuf[sample++] * volumes[1];
307  bufs[4][frame] = aubuf[sample++] * volumes[4];
308  bufs[5][frame] = aubuf[sample++] * volumes[5];
309  bufs[2][frame] = aubuf[sample++] * volumes[2];
310  bufs[3][frame] = aubuf[sample++] * volumes[3];
311  bufs[6][frame] = aubuf[sample++] * volumes[6];
312  bufs[7][frame] = aubuf[sample++] * volumes[7];
313  }
314  }
315  else
316  {
317  for (int frame = 0; frame < nframes; frame++)
318  {
319  // Straightforward de-interleave for all other cases.
320  // Note no channel re-ordering...
321  for (int channel = 0; channel < m_channels; channel++)
322  {
323  bufs[channel][frame] = aubuf[sample++] * volumes[channel];
324  }
325  }
326  }
327 
328 }
329 
330 /* ***************************************************************************
331  Jack Callbacks
332  ****************************************************************************/
333 
334 /* Our Jack callback.
335  Jack will call this from a separate thread whenever it needs "feeding"
336  Simply calls our real code
337 */
338 int AudioOutputJACK::JackCallbackHelper(jack_nframes_t nframes, void *arg)
339 {
340  auto *aoj = static_cast<AudioOutputJACK*>(arg);
341  return aoj->JackCallback(nframes);
342 }
343 
344 /* Useful bit of the callback
345  This callback is called when the sound system requests
346  data. We don't want to block here, because that would
347  just cause dropouts anyway, so we always return whatever
348  data is available. If we haven't received enough, either
349  because we've finished playing or we have a buffer
350  underrun, we play silence to fill the unused space.
351  Return: 0 on success, non zero on error
352 */
353 int AudioOutputJACK::JackCallback(jack_nframes_t nframes)
354 {
355  std::array<float*,JACK_CHANNELS_MAX> bufs {};
356  int bytes_needed = nframes * m_outputBytesPerFrame;
357  int bytes_read = 0;
358 
359  // Check for stale_client set during shutdown callback
361 
362  // Deal with xruns which may have occured
363  // Basically read and discard the data which should have been played
364  int t_jack_xruns = m_jackXruns;
365  for (int i = 0; i < t_jack_xruns; i++)
366  {
367  bytes_read = GetAudioData(m_auBuf, m_fragmentSize, true);
368  VBERROR("Discarded one audio fragment to compensate for xrun");
369  }
370  m_jackXruns -= t_jack_xruns;
371 
372  // Get jack output buffers. Zero out the extra space in the array
373  // to prevent clang-tidy from complaining that DeinterleaveAudio()
374  // can reference a garbage value.
375  int i = 0;
376  for ( ; i < m_channels; i++)
377  bufs[i] = (float*)jack_port_get_buffer(m_ports[i], nframes);
378  for ( ; i < JACK_CHANNELS_MAX; i++)
379  bufs[i] = nullptr;
380 
381  if (m_pauseAudio || m_killAudio)
382  {
383  if (!m_actuallyPaused)
384  {
385  VBAUDIO("JackCallback: audio paused");
387  dispatch(e);
388  m_wasPaused = true;
389  }
390 
391  m_actuallyPaused = true;
392  }
393  else
394  {
395  if (m_wasPaused)
396  {
397  VBAUDIO("JackCallback: Play Event");
399  dispatch(e);
400  m_wasPaused = false;
401  }
402  bytes_read = GetAudioData(m_auBuf, bytes_needed, false);
403  }
404 
405  // Pad with silence
406  if (bytes_needed > bytes_read)
407  {
408  // play silence on buffer underrun
409  memset(m_auBuf + bytes_read, 0, bytes_needed - bytes_read);
410  if (!m_pauseAudio)
411  {
412  VBERROR(QString("Having to insert silence because GetAudioData "
413  "hasn't returned enough data. Wanted: %1 Got: %2")
414  .arg(bytes_needed).arg(bytes_read));
415  }
416  }
417  // Now deinterleave audio (and convert to float)
418  DeinterleaveAudio((float*)m_auBuf, bufs.data(), nframes, m_chanVolumes);
419 
420  if (!m_pauseAudio)
421  {
422  // Output a status event - needed for Music player
423  Status();
424  }
425 
426  return 0;
427 }
428 
429 
430 /* Our Jack XRun callback.
431  Jack will call this from a separate thread whenever an xrun occurs
432  Simply calls our real code
433 */
435 {
436  auto *aoj = static_cast<AudioOutputJACK*>(arg);
437  return aoj->JackXRunCallback();
438 }
439 
440 /* Useful bit of the XRun callback
441  Return: 0 on success, non zero on error
442 */
444 {
445  float delay = jack_get_xrun_delayed_usecs(m_client); // usecs
446  // Increment our counter of "callbacks missed".
447  // All we want to do is chuck away some audio from the ring buffer
448  // to keep our audio output roughly where it should be if we didn't xrun
449  int fragments = (int)ceilf( ((delay / 1000000.0F) * m_sampleRate )
450  / ((float)m_fragmentSize / m_outputBytesPerFrame) );
451  m_jackXruns += fragments; //should be at least 1...
452  VBERROR(QString("Jack XRun Callback: %1 usecs delayed, xruns now %2")
453  .arg(delay).arg(m_jackXruns) );
454 
455  return 0;
456 }
457 
458 /* Our Jack Graph Order Change callback.
459  Jack will call this from a separate thread whenever an xrun occurs
460  Simply calls our real code
461 */
463 {
464  auto *aoj = static_cast<AudioOutputJACK*>(arg);
465  return aoj->JackGraphOrderCallback();
466 }
467 
468 /* Useful bit of the Graph Order Change callback
469  Called when the Jack graph changes. We update our latency info
470  Return: 0 on success, non zero on error
471 */
473 {
474  jack_latency_range_t latency_range;
475  jack_nframes_t max_latency = 0;
476 
477  for (int i = 0; i < m_channels; ++i)
478  {
479  jack_port_get_latency_range( m_ports[i], JackPlaybackLatency, &latency_range );
480  jack_nframes_t port_latency = latency_range.max;
481  if (port_latency > max_latency)
482  max_latency = port_latency;
483  }
484 
485  m_jackLatency = max_latency;
486  VBAUDIO(QString("JACK graph reordered. Maximum latency=%1")
487  .arg(m_jackLatency));
488 
489  return 0;
490 }
491 
492 
493 /* Volume related handling */
495 {
496  int volume = 100;
497  if (m_setInitialVol)
498  {
499  QString controlLabel = gCoreContext->GetSetting("MixerControl", "PCM");
500  controlLabel += "MixerVolume";
501  volume = gCoreContext->GetNumSetting(controlLabel, 80);
502  }
503 
504  m_chanVolumes.fill(volume);
505 }
506 
507 int AudioOutputJACK::GetVolumeChannel(int channel) const
508 {
509  unsigned int vol = 0;
510 
511  if (!m_internalVol)
512  return 100;
513 
514  if (channel < JACK_CHANNELS_MAX)
515  vol = m_chanVolumes[channel];
516 
517  return vol;
518 }
519 
520 void AudioOutputJACK::SetVolumeChannel(int channel, int volume)
521 {
522  if (m_internalVol && (channel < JACK_CHANNELS_MAX))
523  {
524  m_chanVolumes[channel] = volume;
525  if (channel == 0)
526  {
527  // Left
528  m_chanVolumes[2] = volume; // left rear
529  }
530  else if (channel == 1)
531  {
532  // Right
533  m_chanVolumes[3] = volume; // right rear
534  }
535 
536  // LFE and Center
537  m_chanVolumes[4] = m_chanVolumes[5] =
538  (m_chanVolumes[0] + m_chanVolumes[1]) / 2;
539  }
540 }
541 
542 
543 /* We don't need an audio output thread for Jack
544  Everything handled by callbacks here
545  Therefore we can loose all the Start/StopOutputThread, WriteAudio, etc
546 */
548 {
549  return true;
550 }
551 
553 {
554 }
555 
556 // Don't need WriteAudio - this is only for the base class audio loop
557 void AudioOutputJACK::WriteAudio([[maybe_unused]] unsigned char *aubuf,
558  [[maybe_unused]] int size)
559 {
560  // unneeded and unused in JACK
561 }
562 
563 /* **********************************************
564  Jack wrapper and convenience functions
565  ***********************************************/
566 
568 {
569  QString client_name = QString("mythtv_%1").arg(getpid());
570  auto open_options = (jack_options_t)(JackUseExactName | JackNoStartServer);
571  jack_status_t open_status = JackFailure;
572 
573  jack_client_t *client = jack_client_open(client_name.toLatin1().constData(),
574  open_options, &open_status);
575 
576  return client;
577 }
578 
580 {
581  const char **matching_ports = nullptr;
582  unsigned long port_flags=JackPortIsInput;
583  const char *port_name = nullptr;
584 
585  // Have we been given a target port to connect to
586  if (!m_mainDevice.isEmpty())
587  {
588  port_name = m_mainDevice.toLatin1().constData();
589  }
590  else
591  {
592  port_flags |= JackPortIsPhysical;
593  }
594 
595  // list matching ports
596  matching_ports = jack_get_ports(m_client, port_name, nullptr, port_flags);
597  return matching_ports;
598 }
599 
600 
601 bool AudioOutputJACK::JackConnectPorts(const char** matching_ports)
602 {
603  int i=0;
604 
605  // Connect our output ports
606  for (i = 0; i < m_channels; i++)
607  {
608  if (jack_connect(m_client, jack_port_name(m_ports[i]), matching_ports[i]))
609  {
610  Error(LOC + tr("Calling jack_connect failed on port: %1\n").arg(i));
611  return false;
612  }
613  }
614 
615  return true;
616 }
617 
618 void AudioOutputJACK::JackClientClose(jack_client_t **client)
619 {
620  if (*client)
621  {
622  int err = jack_client_close(*client);
623  if (err != 0)
624  Error(LOC + tr("Error closing Jack output device. Error: %1")
625  .arg(err));
626  *client = nullptr;
627  }
628 }
629 
630 /* vim: set expandtab tabstop=4 shiftwidth=4: */
AudioOutputJACK::m_staleClient
jack_client_t * m_staleClient
Definition: audiooutputjack.h:67
AudioOutputJACK::JackClientOpen
static jack_client_t * JackClientOpen(void)
Definition: audiooutputjack.cpp:567
AudioOutputJACK::JackGetPorts
const char ** JackGetPorts(void)
Definition: audiooutputjack.cpp:579
VBAUDIO
#define VBAUDIO(str)
Definition: audiooutputbase.h:20
AudioOutputJACK::JackClientClose
void JackClientClose(jack_client_t **client)
Definition: audiooutputjack.cpp:618
AudioOutputJACK::JackConnectPorts
bool JackConnectPorts(const char **)
Definition: audiooutputjack.cpp:601
OutputEvent
Definition: output.h:31
AudioOutput::Error
void Error(const QString &msg)
Definition: audiooutput.cpp:272
LOC
#define LOC
Definition: audiooutputjack.cpp:35
AudioOutputJACK::OpenDevice
bool OpenDevice(void) override
Definition: audiooutputjack.cpp:116
VolumeBase::m_internalVol
bool m_internalVol
Definition: volumebase.h:41
AudioOutputJACK::m_jackLatency
int m_jackLatency
Definition: audiooutputjack.h:68
AudioOutputBase::KillAudio
void KillAudio(void)
Kill the output thread and cleanup.
Definition: audiooutputbase.cpp:867
AudioOutputJACK::JackGraphOrderCallback
int JackGraphOrderCallback()
Definition: audiooutputjack.cpp:472
AudioOutputJACK
Definition: audiooutputjack.h:19
VBERROR
#define VBERROR(str)
Definition: audiooutputbase.h:23
AudioOutputJACK::StartOutputThread
bool StartOutputThread(void) override
Definition: audiooutputjack.cpp:547
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
AudioOutputJACK::m_ports
jack_port_array m_ports
Definition: audiooutputjack.h:64
MythObservable::dispatch
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
Definition: mythobservable.cpp:73
AudioOutputBase::m_sampleRate
int m_sampleRate
Definition: audiooutputbase.h:180
AudioOutputJACK::StopOutputThread
void StopOutputThread(void) override
Definition: audiooutputjack.cpp:552
AudioOutputBase::m_killAudio
bool m_killAudio
Definition: audiooutputbase.h:197
audiooutputjack.h
AudioSettings
Definition: audiosettings.h:28
jack_vol_array
std::array< int, JACK_CHANNELS_MAX > jack_vol_array
Definition: audiooutputjack.h:17
AudioOutputBase::m_mainDevice
QString m_mainDevice
Definition: audiooutputbase.h:185
AudioOutputJACK::JackCallback
int JackCallback(jack_nframes_t nframes)
Definition: audiooutputjack.cpp:353
AudioOutputJACK::GetOutputSettings
AudioOutputSettings * GetOutputSettings(bool digital) override
Definition: audiooutputjack.cpp:49
mythdate.h
OutputEvent::kStopped
static const Type kStopped
Definition: output.h:67
AudioOutputBase
Definition: audiooutputbase.h:51
AudioSettings::m_init
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:85
AudioOutputJACK::GetBufferedOnSoundcard
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
Definition: audiooutputjack.cpp:237
AudioOutputJACK::JackCallbackHelper
static int JackCallbackHelper(jack_nframes_t nframes, void *arg)
Definition: audiooutputjack.cpp:338
AudioOutputJACK::m_auBuf
unsigned char * m_auBuf
Definition: audiooutputjack.h:70
FORMAT_FLT
@ FORMAT_FLT
Definition: audiooutputsettings.h:31
AudioOutputBase::Reconfigure
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
Definition: audiooutputbase.cpp:469
AudioOutputJACK::WriteAudio
void WriteAudio(unsigned char *aubuf, int size) override
Definition: audiooutputjack.cpp:557
AudioOutputBase::GetAudioData
int GetAudioData(uchar *buffer, int buf_size, bool full_buffer, volatile uint *local_raud=nullptr)
Copy frames from the audiobuffer into the buffer provided.
Definition: audiooutputbase.cpp:1742
AudioOutputJACK::m_jackXruns
int m_jackXruns
Definition: audiooutputjack.h:69
AudioOutputJACK::VolumeInit
void VolumeInit(void)
Definition: audiooutputjack.cpp:494
AudioOutputBase::m_pauseAudio
bool m_pauseAudio
Definition: audiooutputbase.h:199
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:912
OutputEvent::kPlaying
static const Type kPlaying
Definition: output.h:63
JACK_CHANNELS_MAX
static constexpr int8_t JACK_CHANNELS_MAX
Definition: audiooutputjack.h:14
AudioOutputBase::m_channels
int m_channels
Definition: audiooutputbase.h:174
AudioOutputBase::m_setInitialVol
bool m_setInitialVol
Definition: audiooutputbase.h:203
AudioOutputJACK::JackXRunCallbackHelper
static int JackXRunCallbackHelper(void *arg)
Definition: audiooutputjack.cpp:434
AudioOutputBase::Status
virtual void Status(void)
Report status via an OutputEvent.
Definition: audiooutputbase.cpp:1613
AudioOutputBase::m_outputBytesPerFrame
int m_outputBytesPerFrame
Definition: audiooutputbase.h:177
mythcorecontext.h
AudioOutputBase::m_actuallyPaused
bool m_actuallyPaused
Definition: audiooutputbase.h:200
AudioOutputBase::m_wasPaused
bool m_wasPaused
Definition: audiooutputbase.h:201
AudioOutputJACK::DeinterleaveAudio
void DeinterleaveAudio(const float *aubuf, float **bufs, int nframes, const jack_vol_array &channel_volumes)
Definition: audiooutputjack.cpp:251
AudioOutputSettings
Definition: audiooutputsettings.h:48
AudioOutputJACK::m_chanVolumes
jack_vol_array m_chanVolumes
Definition: audiooutputjack.h:65
AudioOutputBase::InitSettings
void InitSettings(const AudioSettings &settings)
Definition: audiooutputbase.cpp:120
AudioOutputJACK::JackGraphOrderCallbackHelper
static int JackGraphOrderCallbackHelper(void *arg)
Definition: audiooutputjack.cpp:462
AudioOutputJACK::CloseDevice
void CloseDevice(void) override
Definition: audiooutputjack.cpp:221
AudioOutputJACK::AudioOutputJACK
AudioOutputJACK(const AudioSettings &settings)
Definition: audiooutputjack.cpp:37
AudioOutputBase::m_fragmentSize
int m_fragmentSize
Definition: audiooutputbase.h:182
AudioOutputJACK::JackXRunCallback
int JackXRunCallback()
Definition: audiooutputjack.cpp:443
AudioOutputJACK::SetVolumeChannel
void SetVolumeChannel(int channel, int volume) override
Definition: audiooutputjack.cpp:520
AudioOutputJACK::~AudioOutputJACK
~AudioOutputJACK() override
Definition: audiooutputjack.cpp:110
AudioOutputJACK::m_client
jack_client_t * m_client
Definition: audiooutputjack.h:66
OutputEvent::kPaused
static const Type kPaused
Definition: output.h:66
AudioOutputJACK::GetVolumeChannel
int GetVolumeChannel(int channel) const override
Definition: audiooutputjack.cpp:507
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:898