#include "audiolayerwidget.h" #include #include #include #include #include // nanosleep y la chapuza de liberar el buffer AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): QGroupBox(parent) , m_suspendResumeButton(0) , m_deviceBox(0) , m_watchDMX(new QTimer(this)) , m_pullTimer(new QTimer(this)) , m_device(QAudioDeviceInfo::defaultOutputDevice()) , m_audioOutput(0) , m_output(0) , m_buffer(BufferSize, 0) , m_decoder(0) , m_progressCounter(new QTime(0,0,0,1)) , m_progressMs(0) , m_currentMedia(" ") , m_running(false) , m_emptyBuffer() { this->setTitle(name); QVBoxLayout *layout = new QVBoxLayout; m_deviceBox = new QComboBox(this); const QAudioDeviceInfo &defaultDeviceInfo = QAudioDeviceInfo::defaultOutputDevice(); m_deviceBox->addItem(defaultDeviceInfo.deviceName(), qVariantFromValue(defaultDeviceInfo)); foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) { if (deviceInfo != defaultDeviceInfo) m_deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo)); } m_deviceBox->setMaximumWidth(250); connect(m_deviceBox,SIGNAL(activated(int)), this, SLOT(deviceChanged(int))); layout->addWidget(m_deviceBox); QHBoxLayout *status = new QHBoxLayout; m_statusLabel = new QLabel; m_statusLabel->setText(STATUS_LABEL); m_statusValue = new QLabel; m_receiveDMX = new QCheckBox("Receiving DMX", this); m_receiveDMX->setChecked(false); status->addWidget(m_receiveDMX); status->addWidget(m_statusLabel); status->addWidget(m_statusValue); layout->addLayout(status); QHBoxLayout *folderLoaded = new QHBoxLayout; m_folderLabel = new QLabel; m_folderLabel->setText(FOLDER_LABEL); m_folderValue = new QLabel; m_folderValue->setMaximumWidth(250); folderLoaded->addWidget(m_folderLabel); folderLoaded->addWidget(m_folderValue); layout->addLayout(folderLoaded); QHBoxLayout *fileLoaded = new QHBoxLayout; m_fileLabel = new QLabel; m_fileLabel->setText(FILE_LABEL); m_fileValue = new QLabel; m_fileValue->setMaximumWidth(250); fileLoaded->addWidget(m_fileLabel); fileLoaded->addWidget(m_fileValue); layout->addLayout(fileLoaded); QHBoxLayout *volumeBox = new QHBoxLayout; m_volumeLabel = new QLabel; m_volumeLabel->setText(tr(VOLUME_LABEL)); m_volumeSlider = new QSlider(Qt::Horizontal); m_volumeSlider->setMinimum(0); m_volumeSlider->setMaximum(80); m_volumeSlider->setSingleStep(1); connect(m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int))); volumeBox->addWidget(m_volumeLabel); volumeBox->addWidget(m_volumeSlider); layout->addLayout(volumeBox); QHBoxLayout *progressTime = new QHBoxLayout; m_progressTimeLabel = new QLabel; m_progressTimeLabel->setText(PROGRESS_TIME_LABEL); m_progressTime = new QTimeEdit; m_progressTime->setDisplayFormat("h:mm:ss:zzz"); m_progressTime->setReadOnly(true); m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); m_progressTime->setMaximumWidth(90); progressTime->addWidget(m_progressTimeLabel); progressTime->addWidget(m_progressTime); m_totalTimeLabel = new QLabel; m_totalTimeLabel->setText(TOTAL_TIME_LABEL); m_totalTimeValue = new QTimeEdit; m_totalTimeValue->setDisplayFormat("h:mm:ss:zzz"); m_totalTimeValue->setReadOnly(true); m_totalTimeValue->setButtonSymbols(QAbstractSpinBox::NoButtons); m_totalTimeValue->setMaximumWidth(90); progressTime->addWidget(m_totalTimeLabel); progressTime->addWidget(m_totalTimeValue); layout->addLayout(progressTime); QHBoxLayout *progressSlider = new QHBoxLayout; m_progressLabel = new QLabel; m_progressLabel->setText(PROGRESS_LABEL); m_progressSlider = new QSlider(Qt::Horizontal); progressSlider->addWidget(m_progressLabel); progressSlider->addWidget(m_progressSlider); layout->addLayout(progressSlider); m_suspendResumeButton = new QPushButton(this); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); layout->addWidget(m_suspendResumeButton); this->setLayout(layout); m_format.setSampleRate(DataSampleRateHz); m_format.setChannelCount(2); m_format.setSampleSize(16); m_format.setCodec("audio/pcm"); m_format.setByteOrder(QAudioFormat::LittleEndian); m_format.setSampleType(QAudioFormat::SignedInt); // Init the silent buffer QAudioBuffer *abuf = new QAudioBuffer(44100, m_format, -1); QByteArray ba((const char*)abuf->data(), abuf->byteCount()); m_emptyBuffer.append(ba); QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); if (!info.isFormatSupported(m_format)) { qWarning("Default format not supported - trying to use nearest"); m_format = info.nearestFormat(m_format); } m_decoder = new AudioDecoder(m_format, this); m_decoder->start(); connect(m_decoder, SIGNAL(totalTimeChanged(qint64)), this, SLOT(durationChanged(qint64))); connect(m_pullTimer, SIGNAL(timeout()), this, SLOT(pullTimerExpired())); connect(m_watchDMX, SIGNAL(timeout()), this, SLOT(watchDMXExpired())); m_watchDMX->start(1000); createAudioOutput(); } AudioLayerWidget::~AudioLayerWidget() { } void AudioLayerWidget::createAudioOutput() { m_audioOutput = new QAudioOutput(m_device, m_format, this); Q_CHECK_PTR(m_audioOutput); m_audioOutput->setNotifyInterval(NOTIFY_INTERVAL); m_audioOutput->setCategory("LibreMediaServer"); connect(m_audioOutput, SIGNAL(notify()), SLOT(notified())); m_output = m_audioOutput->start(); Q_CHECK_PTR(m_output); m_statusValue->setText(PAUSE_LABEL); connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State))); m_pullTimer->start(PULL_TIMER_INTERVAL); } void AudioLayerWidget::deviceChanged(int index) { m_pullTimer->stop(); m_audioOutput->stop(); m_audioOutput->disconnect(this); m_device = m_deviceBox->itemData(index).value(); createAudioOutput(); } void AudioLayerWidget::volumeChanged(int value) { if (m_audioOutput) m_audioOutput->setVolume(qreal(value/100.0f)); } void AudioLayerWidget::setVol(qreal vol) { m_audioOutput->setVolume(vol); m_volumeSlider->blockSignals(true); int volume = vol * 100; m_volumeSlider->setValue(volume); m_volumeSlider->blockSignals(false); } void AudioLayerWidget::loadMedia(QString file) { if (m_currentMedia == file) return; if (QFile::exists(file)){ m_decoder->loadMedia(file); setInitPosition(); m_currentMedia = file; fileLoaded(file); } } void AudioLayerWidget::fileLoaded(QString file) { QStringList list = file.split("/"); int size = list.size(); if (size >= 2) { m_folderValue->setText(list.at(size - 2)); m_fileValue->setText(list.at(size - 1)); } } void AudioLayerWidget::notified() { m_progressMs += m_progressCounter->restart(); m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(m_progressMs)); m_progressSlider->setValue(m_progressMs); /* qDebug() << "bytesFree = " << m_audioOutput->bytesFree() << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs() << ", " << "processedUSecs = " << m_audioOutput->processedUSecs(); */ } void AudioLayerWidget::pullTimerExpired() { if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) { int chunks = m_audioOutput->bytesFree() / m_audioOutput->periodSize(); while (chunks) { if (m_running) { const qint64 len = m_decoder->read(m_buffer.data(), m_audioOutput->periodSize()); if ( len == -1) { stop(); qDebug("End of file %s", this->title().toLatin1().constData()); break; } if (len) { m_output->write(m_buffer.data(), len); } if (len != m_audioOutput->periodSize()) break; } else { m_output->write(m_emptyBuffer.data(), m_audioOutput->periodSize()); } --chunks; } } } void AudioLayerWidget::toggleSuspendResume() { if (m_audioOutput->state() == QAudio::SuspendedState) { resume(); } else if (m_audioOutput->state() == QAudio::ActiveState) { pause(); } else if (m_audioOutput->state() == QAudio::StoppedState) { play(); } else if (m_audioOutput->state() == QAudio::IdleState) { qWarning("%s: IdleState", this->title().toLatin1().constData()); reset(); resume(); } } void AudioLayerWidget::handleStateChanged(QAudio::State state) { if (state == QAudio::SuspendedState) { m_statusValue->setText(PAUSE_LABEL); qDebug("Pause"); } else if (state == QAudio::ActiveState) { m_statusValue->setText(PLAY_LABEL); qDebug("Play"); } else if (m_audioOutput->state() == QAudio::StoppedState) { m_statusValue->setText(STOP_LABEL); qDebug("Stop"); } else if (state == QAudio::IdleState) { m_statusValue->setText(IDDLE_LABEL); qDebug("Iddle"); } } void AudioLayerWidget::durationChanged(qint64 dur) { if (dur == -1) return; m_totalTimeValue->setTime(QTime::fromMSecsSinceStartOfDay(dur)); m_progressSlider->setMaximum(dur); } void AudioLayerWidget::play() { setInitPosition(); // m_pullTimer->start(PULL_TIMER_INTERVAL); // m_audioOutput->resume(); m_running = true; pullTimerExpired(); m_progressCounter->start(); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } void AudioLayerWidget::pause() { // m_pullTimer->stop(); // m_audioOutput->suspend(); m_running = false; m_suspendResumeButton->setText(tr(RESUME_LABEL)); } void AudioLayerWidget::resume() { // m_pullTimer->start(PULL_TIMER_INTERVAL); // m_audioOutput->resume(); m_running = true; pullTimerExpired(); m_progressCounter->start(); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } void AudioLayerWidget::stop() { m_running = false; setInitPosition(); } void AudioLayerWidget::reset() { m_pullTimer->stop(); m_audioOutput->reset(); m_output = m_audioOutput->start(); Q_CHECK_PTR(m_output); } void AudioLayerWidget::setInitPosition() { // reset(); m_decoder->setPos(0); m_progressMs = 0; m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(m_progressMs)); m_progressSlider->setValue(m_progressMs); } void AudioLayerWidget::watchDMXExpired() { m_receiveDMX->setChecked(false); } void AudioLayerWidget::updateWatchDMX(bool b) { m_receiveDMX->setChecked(b); } void AudioLayerWidget::flushBuffer() { // Esto es una chapuza para arreglar que reset() no limpia el buffer realmente }