36#define LOC QString("AOJack: ")
59 auto settings = std::make_unique<AudioOutputSettings>();
64 Error(
LOC + tr(
"Cannot start/connect to jack server "
65 "(to check supported rate/channels)"));
70 rate = jack_get_sample_rate(
m_client);
74 Error(
LOC + tr(
"Unable to retrieve jack server sample rate"));
77 settings->AddSupportedRate(rate);
83 std::unique_ptr<
const char *,
decltype(&jack_free)>
85 if (!matching_ports || !matching_ports.get()[0])
87 Error(
LOC + tr(
"No ports available to connect to"));
93 settings->AddSupportedChannels(i+1);
94 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Adding channels: %1").arg(i+1));
98 return settings.release();
112 {
if (
p->m_client)
p->JackClientClose(&
p->m_client); };
119 Error(
LOC + tr(
"Requested more channels: (%1), than the maximum: %2")
124 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Opening JACK audio device: '%1'.")
135 Error(
LOC + tr(
"Cannot start/connect to jack server"));
140 std::unique_ptr<
const char *,
decltype(&jack_free)>
142 if (!matching_ports || !matching_ports.get()[0])
144 Error(
LOC + tr(
"No ports available to connect to"));
150 while (matching_ports.get()[i])
155 Error(
LOC + tr(
"Not enough ports available to connect to"));
162 QString port_name = QString(
"out_%1").arg(i);
163 m_ports[i] = jack_port_register(
m_client, port_name.toLatin1().constData(),
164 JACK_DEFAULT_AUDIO_TYPE,
165 JackPortIsOutput, 0);
168 Error(
LOC + tr(
"Error while registering new jack port: %1").arg(i));
187 Error(
LOC + tr(
"Error. Unable to set process callback?!"));
189 Error(
LOC + tr(
"Error. Unable to set xrun callback?!"));
191 Error(
LOC + tr(
"Error. Unable to set graph order change callback?!"));
196 Error(
LOC + tr(
"Calling jack_activate failed"));
219 LOG(VB_AUDIO, LOG_INFO,
LOC +
"Jack: Stop Event");
227 int frames_played = jack_frames_since_cycle_start (this->
m_client);
228 LOG(VB_AUDIO | VB_TIMESTAMP, LOG_INFO,
229 QString(
"Stats: frames_since_cycle_start:%1 fragment_size:%2")
249 std::array<float,JACK_CHANNELS_MAX> volumes {};
250 for (
int channel = 0; channel <
m_channels; channel++)
256 volumes[channel] = (float) (( channel_volumes[channel] *
257 channel_volumes[channel] ) /
262 volumes[channel] = 1.0 / 1.0;
268 for (
int frame = 0; frame < nframes; frame++)
270 bufs[0][frame] = aubuf[sample++] * volumes[0];
271 bufs[1][frame] = aubuf[sample++] * volumes[1];
276 for (
int frame = 0; frame < nframes; frame++)
280 bufs[0][frame] = aubuf[sample++] * volumes[0];
281 bufs[1][frame] = aubuf[sample++] * volumes[1];
282 bufs[4][frame] = aubuf[sample++] * volumes[4];
283 bufs[5][frame] = aubuf[sample++] * volumes[5];
284 bufs[2][frame] = aubuf[sample++] * volumes[2];
285 bufs[3][frame] = aubuf[sample++] * volumes[3];
290 for (
int frame = 0; frame < nframes; frame++)
295 bufs[0][frame] = aubuf[sample++] * volumes[0];
296 bufs[1][frame] = aubuf[sample++] * volumes[1];
297 bufs[4][frame] = aubuf[sample++] * volumes[4];
298 bufs[5][frame] = aubuf[sample++] * volumes[5];
299 bufs[2][frame] = aubuf[sample++] * volumes[2];
300 bufs[3][frame] = aubuf[sample++] * volumes[3];
301 bufs[6][frame] = aubuf[sample++] * volumes[6];
302 bufs[7][frame] = aubuf[sample++] * volumes[7];
307 for (
int frame = 0; frame < nframes; frame++)
311 for (
int channel = 0; channel <
m_channels; channel++)
313 bufs[channel][frame] = aubuf[sample++] * volumes[channel];
345 std::array<float*,JACK_CHANNELS_MAX> bufs {};
355 for (
int i = 0; i < t_jack_xruns; i++)
358 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Discarded one audio fragment to compensate for xrun");
367 bufs[i] = (
float*)jack_port_get_buffer(
m_ports[i], nframes);
375 LOG(VB_AUDIO, LOG_INFO,
LOC +
"JackCallback: audio paused");
387 LOG(VB_AUDIO, LOG_INFO,
LOC +
"JackCallback: Play Event");
396 if (bytes_needed > bytes_read)
399 memset(
m_auBuf + bytes_read, 0, bytes_needed - bytes_read);
402 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Having to insert silence because GetAudioData "
403 "hasn't returned enough data. Wanted: %1 Got: %2")
404 .arg(bytes_needed).arg(bytes_read));
435 float delay = jack_get_xrun_delayed_usecs(
m_client);
439 int fragments = (int)ceilf( ((delay / 1000000.0F) *
m_sampleRate )
442 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Jack XRun Callback: %1 usecs delayed, xruns now %2")
464 jack_latency_range_t latency_range;
465 jack_nframes_t max_latency = 0;
469 jack_port_get_latency_range(
m_ports[i], JackPlaybackLatency, &latency_range );
470 jack_nframes_t port_latency = latency_range.max;
471 max_latency = std::max(port_latency, max_latency);
475 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"JACK graph reordered. Maximum latency=%1")
489 controlLabel +=
"MixerVolume";
498 unsigned int vol = 0;
519 else if (channel == 1)
547 [[maybe_unused]]
int size)
558 QString client_name = QString(
"mythtv_%1").arg(getpid());
559 auto open_options = (jack_options_t)(JackUseExactName | JackNoStartServer);
560 jack_status_t open_status = JackFailure;
562 jack_client_t *client = jack_client_open(client_name.toLatin1().constData(),
563 open_options, &open_status);
570 const char **matching_ports =
nullptr;
571 unsigned long port_flags=JackPortIsInput;
572 const char *port_name =
nullptr;
581 port_flags |= JackPortIsPhysical;
585 matching_ports = jack_get_ports(
m_client, port_name,
nullptr, port_flags);
586 return matching_ports;
597 if (jack_connect(
m_client, jack_port_name(
m_ports[i]), matching_ports[i]))
599 Error(
LOC + tr(
"Calling jack_connect failed on port: %1\n").arg(i));
611 int err = jack_client_close(*client);
613 Error(
LOC + tr(
"Error closing Jack output device. Error: %1")
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 OutputEvent.
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 Error(const QString &msg)
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.
static const Type kPlaying
static const Type kStopped
static const Type kPaused
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
static QString cleanup(const QString &str)