36#define LOC QString("AOJack: ")
59 auto settings = std::make_unique<AudioOutputSettings>();
64 QString message {
LOC + tr(
"Cannot start/connect to jack server (to check supported rate/channels)")};
66 LOG(VB_GENERAL, LOG_ERR, message);
71 rate = jack_get_sample_rate(
m_client);
75 QString message {
LOC + tr(
"Unable to retrieve jack server sample rate")};
77 LOG(VB_GENERAL, LOG_ERR, message);
80 settings->AddSupportedRate(rate);
86 std::unique_ptr<
const char *,
decltype(&jack_free)>
88 if (!matching_ports || !matching_ports.get()[0])
90 QString message {
LOC + tr(
"No ports available to connect to")};
92 LOG(VB_GENERAL, LOG_ERR, message);
98 settings->AddSupportedChannels(i+1);
99 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Adding channels: %1").arg(i+1));
103 return settings.release();
117 {
if (
p->m_client)
p->JackClientClose(&
p->m_client); };
124 QString message {
LOC + tr(
"Requested more channels: (%1), than the maximum: %2")
127 LOG(VB_GENERAL, LOG_ERR, message);
131 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Opening JACK audio device: '%1'.")
142 QString message {
LOC + tr(
"Cannot start/connect to jack server")};
144 LOG(VB_GENERAL, LOG_ERR, message);
149 std::unique_ptr<
const char *,
decltype(&jack_free)>
151 if (!matching_ports || !matching_ports.get()[0])
153 QString message {
LOC + tr(
"No ports available to connect to")};
155 LOG(VB_GENERAL, LOG_ERR, message);
161 while (matching_ports.get()[i])
166 QString message {
LOC + tr(
"Not enough ports available to connect to")};
168 LOG(VB_GENERAL, LOG_ERR, message);
175 QString port_name = QString(
"out_%1").arg(i);
176 m_ports[i] = jack_port_register(
m_client, port_name.toLatin1().constData(),
177 JACK_DEFAULT_AUDIO_TYPE,
178 JackPortIsOutput, 0);
181 QString message {
LOC + tr(
"Error while registering new jack port: %1").arg(i)};
183 LOG(VB_GENERAL, LOG_ERR, message);
203 QString message {
LOC + tr(
"Error. Unable to set process callback?!")};
205 LOG(VB_GENERAL, LOG_ERR, message);
209 QString message {
LOC + tr(
"Error. Unable to set xrun callback?!")};
211 LOG(VB_GENERAL, LOG_ERR, message);
215 QString message {
LOC + tr(
"Error. Unable to set graph order change callback?!")};
217 LOG(VB_GENERAL, LOG_ERR, message);
223 QString message {
LOC + tr(
"Calling jack_activate failed")};
225 LOG(VB_GENERAL, LOG_ERR, message);
248 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Jack: Stop Event");
249 Event e(Event::kStopped);
256 int frames_played = jack_frames_since_cycle_start (this->
m_client);
257 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
258 QString(
"Stats: frames_since_cycle_start:%1 fragment_size:%2")
278 std::array<float,JACK_CHANNELS_MAX> volumes {};
279 for (
int channel = 0; channel <
m_channels; channel++)
285 volumes[channel] = (float) (( channel_volumes[channel] *
286 channel_volumes[channel] ) /
291 volumes[channel] = 1.0 / 1.0;
297 for (
int frame = 0; frame < nframes; frame++)
299 bufs[0][frame] = aubuf[sample++] * volumes[0];
300 bufs[1][frame] = aubuf[sample++] * volumes[1];
305 for (
int frame = 0; frame < nframes; frame++)
309 bufs[0][frame] = aubuf[sample++] * volumes[0];
310 bufs[1][frame] = aubuf[sample++] * volumes[1];
311 bufs[4][frame] = aubuf[sample++] * volumes[4];
312 bufs[5][frame] = aubuf[sample++] * volumes[5];
313 bufs[2][frame] = aubuf[sample++] * volumes[2];
314 bufs[3][frame] = aubuf[sample++] * volumes[3];
319 for (
int frame = 0; frame < nframes; frame++)
324 bufs[0][frame] = aubuf[sample++] * volumes[0];
325 bufs[1][frame] = aubuf[sample++] * volumes[1];
326 bufs[4][frame] = aubuf[sample++] * volumes[4];
327 bufs[5][frame] = aubuf[sample++] * volumes[5];
328 bufs[2][frame] = aubuf[sample++] * volumes[2];
329 bufs[3][frame] = aubuf[sample++] * volumes[3];
330 bufs[6][frame] = aubuf[sample++] * volumes[6];
331 bufs[7][frame] = aubuf[sample++] * volumes[7];
336 for (
int frame = 0; frame < nframes; frame++)
340 for (
int channel = 0; channel <
m_channels; channel++)
342 bufs[channel][frame] = aubuf[sample++] * volumes[channel];
374 std::array<float*,JACK_CHANNELS_MAX> bufs {};
384 for (
int i = 0; i < t_jack_xruns; i++)
387 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Discarded one audio fragment to compensate for xrun");
396 bufs[i] = (
float*)jack_port_get_buffer(
m_ports[i], nframes);
404 LOG(VB_AUDIO, LOG_INFO,
LOC +
"JackCallback: audio paused");
405 Event e(Event::kPaused);
416 LOG(VB_AUDIO, LOG_INFO,
LOC +
"JackCallback: Play Event");
417 Event e(Event::kPlaying);
425 if (bytes_needed > bytes_read)
428 memset(
m_auBuf + bytes_read, 0, bytes_needed - bytes_read);
431 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Having to insert silence because GetAudioData "
432 "hasn't returned enough data. Wanted: %1 Got: %2")
433 .arg(bytes_needed).arg(bytes_read));
464 float delay = jack_get_xrun_delayed_usecs(
m_client);
468 int fragments = (int)ceilf( ((delay / 1000000.0F) *
m_sampleRate )
471 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Jack XRun Callback: %1 usecs delayed, xruns now %2")
493 jack_latency_range_t latency_range;
494 jack_nframes_t max_latency = 0;
498 jack_port_get_latency_range(
m_ports[i], JackPlaybackLatency, &latency_range );
499 jack_nframes_t port_latency = latency_range.max;
500 max_latency = std::max(port_latency, max_latency);
504 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"JACK graph reordered. Maximum latency=%1")
518 controlLabel +=
"MixerVolume";
527 unsigned int vol = 0;
548 else if (channel == 1)
576 [[maybe_unused]]
int size)
587 QString client_name = QString(
"mythtv_%1").arg(getpid());
588 auto open_options = (jack_options_t)(JackUseExactName | JackNoStartServer);
589 jack_status_t open_status = JackFailure;
591 jack_client_t *client = jack_client_open(client_name.toLatin1().constData(),
592 open_options, &open_status);
599 const char **matching_ports =
nullptr;
600 unsigned long port_flags=JackPortIsInput;
601 const char *port_name =
nullptr;
610 port_flags |= JackPortIsPhysical;
614 matching_ports = jack_get_ports(
m_client, port_name,
nullptr, port_flags);
615 return matching_ports;
626 if (jack_connect(
m_client, jack_port_name(
m_ports[i]), matching_ports[i]))
628 QString message {
LOC + tr(
"Calling jack_connect failed on port: %1\n").arg(i)};
630 LOG(VB_GENERAL, LOG_ERR, message);
642 int err = jack_client_close(*client);
645 QString message {
LOC + tr(
"Error closing Jack output device. Error: %1")
648 LOG(VB_GENERAL, LOG_ERR, message);
std::array< int, JACK_CHANNELS_MAX > jack_vol_array
static constexpr int8_t JACK_CHANNELS_MAX
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
int m_outputBytesPerFrame
void InitSettings(const AudioSettings &settings)
virtual void Status(void)
Report status via an AudioOutput::Event.
int GetAudioData(uchar *buffer, int buf_size, bool full_buffer, volatile uint *local_raud=nullptr)
Copy frames from the audiobuffer into the buffer provided.
static int JackXRunCallbackHelper(void *arg)
void CloseDevice(void) override
void JackClientClose(jack_client_t **client)
static int JackCallbackHelper(jack_nframes_t nframes, void *arg)
const char ** JackGetPorts(void)
void StopOutputThread(void) override
void SetVolumeChannel(int channel, int volume) override
static int JackGraphOrderCallbackHelper(void *arg)
int JackCallback(jack_nframes_t nframes)
jack_vol_array m_chanVolumes
AudioOutputJACK(const AudioSettings &settings)
AudioOutputSettings * GetOutputSettings(bool digital) override
~AudioOutputJACK() override
jack_client_t * m_staleClient
static jack_client_t * JackClientOpen(void)
void DeinterleaveAudio(const float *aubuf, float **bufs, int nframes, const jack_vol_array &channel_volumes)
void WriteAudio(unsigned char *aubuf, int size) override
int JackGraphOrderCallback()
bool OpenDevice(void) override
bool JackConnectPorts(const char **)
int GetVolumeChannel(int channel) const override
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
bool StartOutputThread(void) override
void dispatchError(const QString &e)
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
QString GetSetting(const QString &key, const QString &defaultval="")
int GetNumSetting(const QString &key, int defaultval=0)
void dispatch(const MythEvent &event)
Dispatch an event to all listeners.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
static QString cleanup(const QString &str)