bool AudioDevOSS::init(const QString &device, const AkAudioCaps &caps) { QMutexLocker mutexLockeer(&this->m_mutex); this->m_deviceFile.setFileName(QString(device) .remove(QRegExp(":Input$|:Output$"))); if (!this->m_deviceFile.open(device.endsWith(":Input")? QIODevice::ReadOnly: QIODevice::WriteOnly)) return false; int format; format = sampleFormats->value(caps.format(), AFMT_QUERY); if (ioctl(this->m_deviceFile.handle(), SNDCTL_DSP_SETFMT, &format) < 0) goto init_fail; int stereo; stereo = caps.channels() > 1? 1: 0; if (ioctl(this->m_deviceFile.handle(), SNDCTL_DSP_STEREO, &stereo) < 0) goto init_fail; int sampleRate; sampleRate = caps.rate(); if (ioctl(this->m_deviceFile.handle(), SNDCTL_DSP_SPEED, &sampleRate) < 0) goto init_fail; if (device.endsWith(":Output")) { int fragment = this->m_fragmentSizeMap.value(device); ioctl(this->m_deviceFile.handle(), SNDCTL_DSP_SETFRAGMENT, &fragment); } this->m_curCaps = caps; return true; init_fail: this->m_deviceFile.close(); return false; }
AkAudioCaps AudioDevOSS::deviceCaps(const QString &device, int *fragmentSize) const { QFile pcmFile(QString(device) .remove(QRegExp(":Input$|:Output$"))); if (!pcmFile.open(device.endsWith(":Input")? QIODevice::ReadOnly: QIODevice::WriteOnly)) return AkAudioCaps(); int formats = AFMT_QUERY; if (ioctl(pcmFile.handle(), SNDCTL_DSP_GETFMTS, &formats) < 0) goto deviceCaps_fail; static const QVector<int> preferredFormats = { AFMT_S16_NE, AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE, AFMT_S8, AFMT_U8 }; int format; format = AFMT_QUERY; for (const auto &fmt: preferredFormats) if (formats & fmt) { format = fmt; break; } if (format == AFMT_QUERY) goto deviceCaps_fail; if (ioctl(pcmFile.handle(), SNDCTL_DSP_SETFMT, &format) < 0) goto deviceCaps_fail; int stereo; stereo = 1; if (ioctl(pcmFile.handle(), SNDCTL_DSP_STEREO, &stereo) < 0) goto deviceCaps_fail; static const QVector<int> preferredSampleRates { 48000, 44100, 22050, 11025, 8000 }; int sampleRate; sampleRate = 0; for (int rate: preferredSampleRates) if (ioctl(pcmFile.handle(), SNDCTL_DSP_SPEED, &rate) >= 0){ sampleRate = rate; break; } if (sampleRate < 1) goto deviceCaps_fail; int channels; channels = stereo? 2: 1; AkAudioCaps::SampleFormat sampleFormat; sampleFormat = sampleFormats->key(format, AkAudioCaps::SampleFormat_none); if (fragmentSize && device.endsWith(":Output")) { // Set the buffer to a maximum of 1024 samples. int bufferSize; bufferSize = BUFFER_SIZE * channels * AkAudioCaps::bitsPerSample(sampleFormat) / 8; // Let's try setting the fragmet to just 2 pieces, and the half of the // buffer size, for low latency. int fragment; fragment = (2 << 16) | (bufferSize / 2); ioctl(pcmFile.handle(), SNDCTL_DSP_SETFRAGMENT, &fragment); // Let's see what OSS did actually set, audio_buf_info info; ioctl(pcmFile.handle(), SNDCTL_DSP_GETOSPACE, &info); *fragmentSize = info.fragsize > 0? ((bufferSize / info.fragsize) << 16) | info.fragsize: 0; } pcmFile.close(); { AkAudioCaps audioCaps; audioCaps.isValid() = true; audioCaps.format() = sampleFormat; audioCaps.bps() = AkAudioCaps::bitsPerSample(audioCaps.format()); audioCaps.channels() = channels; audioCaps.rate() = int(sampleRate); audioCaps.layout() = AkAudioCaps::defaultChannelLayout(audioCaps.channels()); audioCaps.align() = false; return audioCaps; } deviceCaps_fail: pcmFile.close(); return AkAudioCaps(); }
AudioDevJack::AudioDevJack(QObject *parent): AudioDev(parent) { this->m_curChannels = 0; this->m_curSampleRate = 0; this->m_maxBufferSize = 0; this->m_isInput = false; this->m_client = NULL; this->m_descriptions = { {":jackinput:" , "JACK Audio Connection Kit Input" }, {":jackoutput:", "JACK Audio Connection Kit Output"}, }; auto appName = QCoreApplication::applicationName() + QString("_%1").arg(Ak::id()); int maxNameSize = jack_client_name_size() - 1; if (appName.size() > maxNameSize) appName = appName.mid(0, maxNameSize); jack_status_t status; this->m_client = jack_client_open(appName.toStdString().c_str(), JackNullOption, &status); if (!this->m_client) { this->m_error = jackErrorCodes->value(status); Q_EMIT this->errorChanged(this->m_error); return; } // Setup callbacks jack_set_process_callback(this->m_client, AudioDevJack::onProcessCallback, this); jack_on_shutdown(this->m_client, AudioDevJack::onShutdownCallback, this); AkAudioCaps audioCaps; audioCaps.isValid() = true; audioCaps.format() = AkAudioCaps::SampleFormat_flt; audioCaps.bps() = AkAudioCaps::bitsPerSample(audioCaps.format()); audioCaps.rate() = int(jack_get_sample_rate(this->m_client)); audioCaps.layout() = AkAudioCaps::defaultChannelLayout(audioCaps.channels()); audioCaps.align() = false; QMap<QString, JackPortFlags> portTypeMap = { {":jackinput:" , JackPortIsOutput}, {":jackoutput:", JackPortIsInput } }; // Query the number orr channels for (auto deviceId: portTypeMap.keys()) { auto ports = jack_get_ports(this->m_client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | portTypeMap[deviceId]); int channels = 0; for (auto portName = ports; portName && *portName; portName++, channels++) this->m_devicePorts[deviceId] << *portName; if (ports) jack_free(ports); if (channels > 0) { audioCaps.channels() = channels; this->m_caps[deviceId] = audioCaps; } } }
bool AudioDevJack::init(const QString &device, const AkAudioCaps &caps) { if (!this->m_caps.contains(device) || caps.channels() < 1 || caps.channels() > 2 || caps.rate() != int(jack_get_sample_rate(this->m_client)) || caps.format() != AkAudioCaps::SampleFormat_flt) return false; this->m_appPorts.clear(); this->m_curChannels = 0; this->m_curSampleRate = 0; this->m_buffer.clear(); QString portName = device == ":jackinput:"? "input": "output"; JackPortFlags portFlags = device == ":jackinput:"? JackPortIsInput: JackPortIsOutput; // Create ports for sending/receiving data for (int channel = 0; channel < caps.channels(); channel++) { auto port = jack_port_register(this->m_client, QString("%1_%2") .arg(portName) .arg(channel + 1).toStdString().c_str(), JACK_DEFAULT_AUDIO_TYPE, portFlags, 0); if (port) this->m_appPorts << port; } if (this->m_appPorts.size() < caps.channels()) { this->m_error = "AudioDevJack::init: No more JACK ports available"; Q_EMIT this->errorChanged(this->m_error); this->uninit(); return false; } auto bufferSize = jack_get_buffer_size(this->m_client); // Activate JACK client if (auto error = jack_status_t(jack_activate(this->m_client))) { this->m_error = jackErrorCodes->value(error); Q_EMIT this->errorChanged(this->m_error); this->uninit(); return false; } if (caps.channels() == 1) { if (device == ":jackinput:") { for (auto port: this->m_devicePorts[device]) jack_connect(this->m_client, port.toStdString().c_str(), jack_port_name(this->m_appPorts.first())); } else { for (auto port: this->m_devicePorts[device]) jack_connect(this->m_client, jack_port_name(this->m_appPorts.first()), port.toStdString().c_str()); } } else { auto ports = this->m_devicePorts[device]; if (device == ":jackinput:") { for (int i = 0; i < this->m_appPorts.size(); i++) jack_connect(this->m_client, ports[i].toStdString().c_str(), jack_port_name(this->m_appPorts[i])); } else { for (int i = 0; i < this->m_appPorts.size(); i++) jack_connect(this->m_client, jack_port_name(this->m_appPorts[i]), ports[i].toStdString().c_str()); } } this->m_curDevice = device; this->m_curChannels = caps.channels(); this->m_curSampleRate = caps.rate(); this->m_maxBufferSize = int(2 * sizeof(jack_default_audio_sample_t) * uint(caps.channels()) * bufferSize); this->m_isInput = device == ":jackinput:"; return true; }