From cccd987bddf25783a0f8c14de201f05a2d4354b5 Mon Sep 17 00:00:00 2001 From: santi Date: Thu, 10 Jul 2014 01:51:29 +0200 Subject: [PATCH 1/3] Qt5 upgrade and new audio engine based in QtMultimedia. New GUI. --- .gitignore | 46 ++++ puredata/layer_audio.pd | 84 -------- puredata/lms-audio.pd | 134 ------------ src/audiodecoder.cpp | 168 +++++++++++++++ src/audiodecoder.h | 45 ++++ src/audiolayerwidget.cpp | 273 +++++++++++++++++++++-- src/audiolayerwidget.h | 88 +++++--- src/audiomasterwidget.h | 4 + src/audiomotor.cpp | 382 --------------------------------- src/audiomotor.h | 143 ------------ src/audiowidget.cpp | 36 +++- src/audiowidget.h | 11 +- src/defines.h | 49 +++++ src/libremediaserver-audio.cpp | 69 ++---- src/libremediaserver-audio.h | 32 +-- src/libremediaserver-audio.pro | 12 +- src/main.cpp | 47 ++-- src/medialibrary.cpp | 6 +- src/medialibrary.h | 23 +- src/olathread.cpp | 18 +- src/olathread.h | 24 +-- src/settings.cpp | 9 - src/settings.h | 4 +- 23 files changed, 724 insertions(+), 983 deletions(-) create mode 100644 .gitignore delete mode 100755 puredata/layer_audio.pd delete mode 100755 puredata/lms-audio.pd create mode 100644 src/audiodecoder.cpp create mode 100644 src/audiodecoder.h delete mode 100644 src/audiomotor.cpp delete mode 100644 src/audiomotor.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ec2837 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# C++ objects and libs + +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.dll +*.dylib + +# Qt-es + +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.moc +moc_*.cpp +qrc_*.cpp +ui_*.h +Makefile* +*-build-* + +# QtCreator + +*.autosave + +# binarios + +puredata/pd +puredata/externals/** + +# Folders + +bin/** +tcl/** +src/debug/** +src/release/** +log/** + +# Backups + +*~ +*.autosave diff --git a/puredata/layer_audio.pd b/puredata/layer_audio.pd deleted file mode 100755 index fa44c35..0000000 --- a/puredata/layer_audio.pd +++ /dev/null @@ -1,84 +0,0 @@ -#N canvas 70 180 904 466 10; -#N canvas 1132 494 547 527 audio_player 1; -#X msg 203 93 start; -#X msg 259 95 stop; -#X obj 119 140 oggread~; -#X msg 301 92 resume; -#X obj 22 9 inlet; -#X obj 219 10 r \$0-c5; -#X obj 77 213 *~ 0; -#X obj 136 213 *~ 0; -#X obj 158 450 dac~; -#X obj 210 172 r \$0-c1; -#X obj 211 234 r \$0-c2; -#X text 208 155 Volumen; -#X text 266 233 Pan; -#X obj 219 71 select 0 1 2; -#X msg 119 101 seek \$1; -#X obj 119 36 r \$0-c8; -#X obj 119 67 * 0.01; -#X obj 210 358 line~; -#X msg 210 337 \$1 1; -#X obj 117 350 line~; -#X msg 117 329 \$1 1; -#X obj 194 378 *~; -#X obj 101 375 *~; -#X floatatom 309 148 5 0 0 0 - - -; -#X obj 333 386 outlet; -#X obj 333 347 int; -#X obj 335 305 * 100; -#X obj 117 297 1 - \$1; -#X connect 0 0 2 0; -#X connect 1 0 2 0; -#X connect 2 0 6 0; -#X connect 2 1 7 0; -#X connect 2 2 23 0; -#X connect 3 0 2 0; -#X connect 4 0 2 0; -#X connect 5 0 13 0; -#X connect 6 0 22 0; -#X connect 7 0 21 0; -#X connect 9 0 7 1; -#X connect 9 0 6 1; -#X connect 9 0 26 0; -#X connect 10 0 18 0; -#X connect 10 0 27 0; -#X connect 13 0 0 0; -#X connect 13 1 1 0; -#X connect 13 2 3 0; -#X connect 14 0 2 0; -#X connect 15 0 16 0; -#X connect 16 0 14 0; -#X connect 17 0 21 1; -#X connect 18 0 17 0; -#X connect 19 0 22 1; -#X connect 20 0 19 0; -#X connect 21 0 8 1; -#X connect 22 0 8 0; -#X connect 25 0 24 0; -#X connect 26 0 25 0; -#X connect 27 0 20 0; -#X restore 219 -234 pd audio_player; -#X obj 19 -18 s \$0-c1; -#X obj 79 -19 s \$0-c2; -#X obj 135 -18 s \$0-c3; -#X obj 197 -16 s \$0-c4; -#X obj 268 -15 s \$0-c5; -#X obj 335 -234 outlet; -#X text 465 -274 c1 Vol Coarse c2 pan c3 folder c4 file c5 playback -c6 Control c7 Volumen fino 8 Entry point Coarse 9 Entry point fine -; -#X obj 106 -273 inlet; -#X obj 219 -270 inlet; -#X obj 106 -160 route 0 1 2 3 4; -#X obj 619 -56 outlet; -#X connect 0 0 11 0; -#X connect 8 0 10 0; -#X connect 9 0 0 0; -#X connect 9 0 6 0; -#X connect 10 0 1 0; -#X connect 10 1 2 0; -#X connect 10 2 3 0; -#X connect 10 3 4 0; -#X connect 10 4 5 0; -#X coords 0 466 1 465 0 0 0; diff --git a/puredata/lms-audio.pd b/puredata/lms-audio.pd deleted file mode 100755 index 8ad9ce3..0000000 --- a/puredata/lms-audio.pd +++ /dev/null @@ -1,134 +0,0 @@ -#N canvas 1 460 1700 561 10; -#X obj -297 249 layer_audio; -#X msg -673 125 \; pd dsp 1; -#X obj -400 57 loadbang; -#X msg -297 279 send 1 \$1; -#X obj -260 502 netsend; -#X msg -149 270 send 2 \$1; -#X msg -5 272 send 3 \$1; -#X msg 140 270 send 4 \$1; -#X msg 279 269 send 5 \$1; -#X msg 429 273 send 6 \$1; -#X msg 571 272 send 7 \$1; -#X msg 732 266 send 8 \$1; -#X obj -539 202 delay 50; -#X msg -539 224 send 0; -#X msg -704 203 connect localhost 9198; -#N canvas 1 110 1192 300 receive 0; -#X floatatom 380 -469 5 0 0 0 - - -; -#X obj 412 -371 route 201 202 203 204 205 206 207 208; -#X obj -197 -369 send dmx1; -#X obj 332 -369 send dmx8; -#X obj 259 -369 send dmx7; -#X obj 183 -369 send dmx6; -#X obj 110 -369 send dmx5; -#X obj 25 -369 send dmx4; -#X obj -48 -369 send dmx3; -#X obj -124 -369 send dmx2; -#X obj 118 -280 s file1; -#X obj 178 -280 s file2; -#X obj 240 -280 s file3; -#X obj 300 -280 s file4; -#X obj 358 -281 s file5; -#X obj 418 -281 s file6; -#X obj 480 -281 s file7; -#X obj 540 -281 s file8; -#X obj 207 -451 route 0 1 2 3 4 5 6 7; -#X obj 207 -486 netreceive 9197; -#X connect 1 0 10 0; -#X connect 1 1 11 0; -#X connect 1 2 12 0; -#X connect 1 3 13 0; -#X connect 1 4 14 0; -#X connect 1 5 15 0; -#X connect 1 6 16 0; -#X connect 1 7 17 0; -#X connect 18 0 2 0; -#X connect 18 1 9 0; -#X connect 18 2 8 0; -#X connect 18 3 7 0; -#X connect 18 4 6 0; -#X connect 18 5 5 0; -#X connect 18 6 4 0; -#X connect 18 7 3 0; -#X connect 18 8 1 0; -#X connect 19 0 18 0; -#X connect 19 1 0 0; -#X restore -156 105 pd receive; -#X obj -297 227 r dmx1; -#X obj -261 192 r file1; -#X obj -149 228 r dmx2; -#X obj -5 229 r dmx3; -#X obj 29 180 r file3; -#X obj 140 229 r dmx4; -#X obj 170 179 r file4; -#X obj 279 226 r dmx5; -#X obj 353 226 r file 5; -#X obj 429 230 r dmx6; -#X obj 503 228 r file6; -#X obj 571 227 r dmx7; -#X obj 645 226 r file 7; -#X obj 730 224 r dmx8; -#X obj 804 224 r file8; -#X obj -111 199 r file2; -#X msg -784 99 \; pd dsp 0; -#X obj -673 103 delay 100; -#X obj -5 251 layer_audio; -#X obj 140 250 layer_audio; -#X obj 279 249 layer_audio; -#X obj -149 251 layer_audio; -#X obj 429 253 layer_audio; -#X obj 571 250 layer_audio; -#X obj 730 246 layer_audio; -#X msg -269 326 send 9 0 1 \$1; -#X msg -126 337 send 9 0 2 \$1; -#X msg 4 318 send 9 0 3 \$1; -#X msg 147 323 send 9 0 4 \$1; -#X connect 0 0 3 0; -#X connect 0 1 41 0; -#X connect 2 0 14 0; -#X connect 2 0 12 0; -#X connect 2 0 32 0; -#X connect 2 0 33 0; -#X connect 3 0 4 0; -#X connect 5 0 4 0; -#X connect 6 0 4 0; -#X connect 7 0 4 0; -#X connect 8 0 4 0; -#X connect 9 0 4 0; -#X connect 10 0 4 0; -#X connect 11 0 4 0; -#X connect 12 0 13 0; -#X connect 13 0 4 0; -#X connect 14 0 4 0; -#X connect 16 0 0 0; -#X connect 17 0 0 1; -#X connect 18 0 37 0; -#X connect 19 0 34 0; -#X connect 20 0 34 1; -#X connect 21 0 35 0; -#X connect 22 0 35 1; -#X connect 23 0 36 0; -#X connect 24 0 36 1; -#X connect 25 0 38 0; -#X connect 26 0 38 1; -#X connect 27 0 39 0; -#X connect 28 0 39 1; -#X connect 29 0 40 0; -#X connect 30 0 40 1; -#X connect 31 0 37 1; -#X connect 33 0 1 0; -#X connect 34 0 6 0; -#X connect 34 1 43 0; -#X connect 35 0 7 0; -#X connect 35 1 44 0; -#X connect 36 0 8 0; -#X connect 37 0 5 0; -#X connect 37 1 42 0; -#X connect 38 0 9 0; -#X connect 39 0 10 0; -#X connect 40 0 11 0; -#X connect 41 0 4 0; -#X connect 42 0 4 0; -#X connect 43 0 4 0; -#X connect 44 0 4 0; diff --git a/src/audiodecoder.cpp b/src/audiodecoder.cpp new file mode 100644 index 0000000..41542ae --- /dev/null +++ b/src/audiodecoder.cpp @@ -0,0 +1,168 @@ +#include "audiodecoder.h" + +#include +#include +#include + +AudioDecoder::AudioDecoder(const QAudioFormat &format, + qint64 durationUs, + int sampleRate, + QObject *parent) + : QIODevice(parent) + , m_pos(0) +{ + m_decoder = new QAudioDecoder(); + m_decoder->setAudioFormat(format); + connect(m_decoder, SIGNAL(error(QAudioDecoder::Error)), + this, SLOT(errorDecoding(QAudioDecoder::Error))); + connect(m_decoder, SIGNAL(bufferReady()), + this, SLOT(readBuffer())); + + generateData(format, durationUs, sampleRate); +} + +AudioDecoder::AudioDecoder(const QAudioFormat &format, + QObject *parent) + : QIODevice(parent) + , m_pos(0) +{ + m_decoder = new QAudioDecoder(); + m_decoder->setAudioFormat(format); + connect(m_decoder, SIGNAL(error(QAudioDecoder::Error)), + this, SLOT(errorDecoding(QAudioDecoder::Error))); + connect(m_decoder, SIGNAL(bufferReady()), + this, SLOT(readBuffer())); + + connect(m_decoder, SIGNAL(finished()), + this, SLOT(decoderFinished())); + + connect(m_decoder, SIGNAL(durationChanged(qint64)), + this, SLOT(trackTimeChanged(qint64))); +} + +AudioDecoder::~AudioDecoder() +{ + +} + +void AudioDecoder::start() +{ + open(QIODevice::ReadOnly); +} + +void AudioDecoder::stop() +{ + m_pos = 0; + close(); +} + +void AudioDecoder::loadMedia(QString file) +{ + m_buffer.clear(); + m_decoder->setSourceFilename(file); + m_decoder->start(); +} + +void AudioDecoder::generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate) +{ + + const int channelBytes = format.sampleSize() / 8; + const int sampleBytes = format.channelCount() * channelBytes; + qint64 length = (format.sampleRate() * format.channelCount() * (format.sampleSize() / 8)) + * durationUs / 100000; + Q_ASSERT(length % sampleBytes == 0); + Q_UNUSED(sampleBytes) // suppress warning in release builds + m_buffer.resize(length); + unsigned char *ptr = reinterpret_cast(m_buffer.data()); + int sampleIndex = 0; + while (length) { + const qreal x = qSin(2 * M_PI * sampleRate * qreal(sampleIndex % format.sampleRate()) / format.sampleRate()); + for (int i=0; i(x * 32767); + if (format.byteOrder() == QAudioFormat::LittleEndian) + qToLittleEndian(value, ptr); + ptr += channelBytes; + length -= channelBytes; + } + ++sampleIndex; + } +} + +void AudioDecoder::readBuffer() +{ + // Extract all samples avalaible + while(m_decoder->bufferAvailable()) + { + QAudioBuffer qbuf = m_decoder->read(); + QByteArray ba((const char*)qbuf.data(), qbuf.byteCount()); + m_buffer.append(ba); + } +} + +void AudioDecoder::errorDecoding(QAudioDecoder::Error msg) +{ +// qWarning("Error Decoding "); + switch (msg) { + case QAudioDecoder::NoError: + break; + case QAudioDecoder::ResourceError: + qWarning() << "A media resource couldn't be resolved: " << m_decoder->sourceFilename(); + break; + case QAudioDecoder::FormatError: + qWarning() << "The format of a media resource isn't supported: " << m_decoder->sourceFilename();; + break; + case QAudioDecoder::AccessDeniedError: + qWarning() << "There are not the appropriate permissions to play a media resource" << m_decoder->sourceFilename();; + break; + case QAudioDecoder::ServiceMissingError: + qWarning() << "A valid playback service was not found, playback cannot proceed."; + break; + } +} + +qint64 AudioDecoder::readData(char *data, qint64 len) +{ + if (m_buffer.size() == 0) + return 0; + qint64 total = 0; + while (len - total > 0) { + const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total); +// qWarning() << "Buffer Size: " << m_buffer.size() << "m_pos: " << m_pos; + memcpy(data + total, m_buffer.constData() + m_pos, chunk); + // Controla Final del track + if ( (m_pos + chunk ) >= ( m_buffer.size() ) + && m_decoder->state() != QAudioDecoder::DecodingState ) + return (-1); + m_pos = (m_pos + chunk) % m_buffer.size(); // Esto reseta a 0 si alcanza el final y hace loop + total += chunk; + } + return total; +} + +qint64 AudioDecoder::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data); + Q_UNUSED(len); + return 0; +} + +qint64 AudioDecoder::bytesAvailable() const +{ + return m_buffer.size() + QIODevice::bytesAvailable(); +} + +void AudioDecoder::decoderFinished() +{ + qDebug() << "AudioDecoder: Decoding file finished; " << m_decoder->sourceFilename(); + emit fileLoaded(m_decoder->sourceFilename()); +} + +void AudioDecoder::trackTimeChanged(qint64 dur) +{ + emit totalTimeChanged(dur); +} + +void AudioDecoder::setPos(qint64 pos) +{ + m_pos = pos; +} diff --git a/src/audiodecoder.h b/src/audiodecoder.h new file mode 100644 index 0000000..518cc5c --- /dev/null +++ b/src/audiodecoder.h @@ -0,0 +1,45 @@ +#ifndef AUDIODECODER_H +#define AUDIODECODER_H + +#include +#include +#include + +class AudioDecoder : public QIODevice +{ + Q_OBJECT + +public: + AudioDecoder(const QAudioFormat &format, qint64 durationUs, int sampleRate, QObject *parent); + AudioDecoder(const QAudioFormat &format, QObject *parent); + ~AudioDecoder(); + + void start(); + void stop(); + + qint64 readData(char *data, qint64 maxlen); + qint64 writeData(const char *data, qint64 len); + qint64 bytesAvailable() const; + + void loadMedia(QString file); + + void setPos(qint64 pos); + +private: + void generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate); + qint64 m_pos; + QByteArray m_buffer; + QAudioDecoder *m_decoder; + +private slots: + void readBuffer(); + void errorDecoding(QAudioDecoder::Error msg); + void decoderFinished(); + void trackTimeChanged(qint64 dur); + +signals: + void totalTimeChanged(qint64 dur); + void fileLoaded(QString file); +}; + +#endif // AUDIODECODER_H diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 1ee75b8..177dc33 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -1,22 +1,120 @@ #include "audiolayerwidget.h" +#include +#include +#include +#include + AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): QGroupBox(parent) + , m_pullTimer(new QTimer(this)) + , m_suspendResumeButton(0) + , m_deviceBox(0) + , m_output(0) + , m_device(QAudioDeviceInfo::defaultOutputDevice()) + , m_audioOutput(0) + , m_buffer(BufferSize, 0) { - folder = new QLabel(this); - file = new QLabel(this); - status = new QLabel(this); - vol = new QSlider(this); - vol->setMaximum(99); - mute = new QCheckBox(this); + this->setTitle(name); - QVBoxLayout *vbox = new QVBoxLayout; - vbox->addWidget(folder); - vbox->addWidget(file); - vbox->addWidget(status); - vbox->addWidget(vol); - vbox->addWidget(mute); - this->setLayout(vbox); + 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)); + } + 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; + 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; + 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; + fileLoaded->addWidget(m_fileLabel); + fileLoaded->addWidget(m_fileValue); + layout->addLayout(fileLoaded); + + m_suspendResumeButton = new QPushButton(this); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); + connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); + layout->addWidget(m_suspendResumeButton); + + 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(100); + 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 *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); + + QHBoxLayout *progressTime = new QHBoxLayout; + m_progressTimeLabel = new QLabel; + m_progressTimeLabel->setText(PROGRESS_TIME_LABEL); + m_progressTimeValue = new QLabel; + m_progressTimeValue->setText("0:00:00:000"); + progressTime->addWidget(m_progressTimeLabel); + progressTime->addWidget(m_progressTimeValue); + layout->addLayout(progressTime); + + QHBoxLayout *totalTime = new QHBoxLayout; + m_totalTimeLabel = new QLabel; + m_totalTimeLabel->setText(TOTAL_TIME_LABEL); + m_totalTimeValue = new QLabel; + totalTime->addWidget(m_totalTimeLabel); + totalTime->addWidget(m_totalTimeValue); + layout->addLayout(totalTime); + + 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); + + 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); + connect(m_decoder, SIGNAL(totalTimeChanged(qint64)), + this, SLOT(durationChanged(qint64))); + connect(m_pullTimer, SIGNAL(timeout()), + this, SLOT(pullTimerExpired())); + createAudioOutput(); } AudioLayerWidget::~AudioLayerWidget() @@ -24,3 +122,152 @@ AudioLayerWidget::~AudioLayerWidget() } +void AudioLayerWidget::createAudioOutput() +{ + m_audioOutput = new QAudioOutput(m_device, m_format, this); + m_audioOutput->setNotifyInterval(100); + connect(m_audioOutput, SIGNAL(notify()), SLOT(notified())); + connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State))); + m_decoder->start(); +} + +void AudioLayerWidget::deviceChanged(int index) +{ + m_pullTimer->stop(); + m_decoder->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) +{ + stop(); + if (QFile::exists(file)){ + m_decoder->loadMedia(file); + fileLoaded(file); + } +} + +void AudioLayerWidget::notified() +{ + QTime real(0,0,0,0); + qint64 ms = m_audioOutput->processedUSecs() / 1000 ; + real = real.addMSecs(ms); + m_progressTimeValue->setText(real.toString("h:mm:ss:zzz")); + m_progressSlider->setValue(ms); +/* 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) { + const qint64 len = m_decoder->read(m_buffer.data(), m_audioOutput->periodSize()); + if ( len == -1) { + qDebug() << "End of file"; + stop(); + break; + } + if (len) { + m_output->write(m_buffer.data(), len); + } + if (len != m_audioOutput->periodSize()) + break; + --chunks; + } + } +} + +void AudioLayerWidget::toggleSuspendResume() +{ + if (m_audioOutput->state() == QAudio::SuspendedState) { +// qDebug() << "status: Suspended, resume()"; + m_audioOutput->resume(); + m_pullTimer->start(20); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); + m_statusValue->setText(PLAY_LABEL); + } else if (m_audioOutput->state() == QAudio::ActiveState) { +// qDebug() << "status: Active, suspend()"; + m_audioOutput->suspend(); + m_pullTimer->stop(); + m_suspendResumeButton->setText(tr(RESUME_LABEL)); + m_statusValue->setText(PAUSE_LABEL); + } else if (m_audioOutput->state() == QAudio::StoppedState) { +// qDebug() << "status: Stopped, resume()"; + play(); + } else if (m_audioOutput->state() == QAudio::IdleState) { +// qDebug() << "status: IdleState"; + m_statusValue->setText(IDDLE_LABEL); + } +} + +void AudioLayerWidget::handleStateChanged(QAudio::State state) +{ + qDebug() << this->title() << " state = " << state; +} + +void AudioLayerWidget::durationChanged(qint64 dur) +{ + if (dur == -1) + return; + QTime temp(0,0,0,0); + QTime real = temp.addMSecs(dur); + m_totalTimeValue->setText(real.toString("h:mm:ss:zzz")); + m_progressSlider->setMaximum(dur); + qDebug() << this->title() << " Duration Changed: " << dur; +} + +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::play() +{ + m_decoder->setPos(0); + m_output = m_audioOutput->start(); + m_pullTimer->start(20); + m_statusValue->setText(PLAY_LABEL); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); +} + +void AudioLayerWidget::pause() +{ + toggleSuspendResume(); +} + +void AudioLayerWidget::stop() +{ + m_pullTimer->stop(); + m_audioOutput->suspend(); + m_audioOutput->reset(); + m_decoder->setPos(0); + m_statusValue->setText(STOP_LABEL); + m_suspendResumeButton->setText(tr(RESUME_LABEL)); +} diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index 3384dcf..d380b5b 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -1,13 +1,17 @@ #ifndef AUDIOLAYERWIDGET_H #define AUDIOLAYERWIDGET_H -#include +#include +#include +#include +#include +#include +#include +#include +#include -//#include "ui_audiolayerwidget.h" -/* -namespace Ui { -class AudioLayerWidget; -}*/ +#include "audiodecoder.h" +#include "defines.h" class AudioLayerWidget : public QGroupBox { @@ -15,31 +19,65 @@ class AudioLayerWidget : public QGroupBox public: - AudioLayerWidget(QWidget *parent, QString name); + explicit AudioLayerWidget(QWidget *parent = 0, QString name = "Layer"); ~AudioLayerWidget(); - inline void setFile(QString file) const - { - this->file->setText(file); - } + void loadMedia(QString file); + void play(); + void stop(); + void pause(); + void setVol(qreal vol); - inline void setFolder(QString folder) const - { - this->folder->setText(folder); - } +public slots: - inline void setVol(float vol) const - { - this->vol->setValue(vol); - } + void toggleSuspendResume(); + void volumeChanged(int vol); - private: - QLabel *file; - QLabel *folder; - QSlider *vol; - QCheckBox *mute; - QLabel *status; + QPushButton *m_suspendResumeButton; + QComboBox *m_deviceBox; + + QLabel *m_statusLabel; + QLabel * m_statusValue; + + QLabel *m_volumeLabel; + QSlider *m_volumeSlider; + + QLabel * m_progressLabel; + QSlider *m_progressSlider; + + QLabel *m_progressTimeLabel; + QLabel *m_progressTimeValue; + + QLabel *m_totalTimeLabel; + QLabel *m_totalTimeValue; + + QLabel *m_fileLabel; + QLabel *m_fileValue; + + QLabel * m_folderLabel; + QLabel * m_folderValue; + + QTimer *m_pullTimer; + QAudioDeviceInfo m_device; + QAudioOutput *m_audioOutput; + QIODevice *m_output; // not owned + QAudioFormat m_format; + QByteArray m_buffer; + AudioDecoder *m_decoder; + + void createAudioOutput(); + +private slots: + void notified(); + void pullTimerExpired(); + void fileLoaded(QString file); + void handleStateChanged(QAudio::State state); + void deviceChanged(int index); + void durationChanged(qint64 dur); + }; + + #endif // AUDIOLAYERWIDGET_H diff --git a/src/audiomasterwidget.h b/src/audiomasterwidget.h index e9b374f..d87a237 100644 --- a/src/audiomasterwidget.h +++ b/src/audiomasterwidget.h @@ -3,6 +3,10 @@ #include #include +#include +#include +#include +#include //#include "ui_audiomasterwidget.h" diff --git a/src/audiomotor.cpp b/src/audiomotor.cpp deleted file mode 100644 index 49c646b..0000000 --- a/src/audiomotor.cpp +++ /dev/null @@ -1,382 +0,0 @@ -#include "audiomotor.h" - -#define PAUSE 2 -#define PLAY 0 -#define STOP 1 - -#define MAX_DATA = 1024; - -AudioMotor *AudioMotor::_instance = 0; - -AudioMotor *AudioMotor::getInstance() { - if (_instance == 0) { - _instance = new AudioMotor(); - Q_CHECK_PTR(_instance); - } - return _instance; -} - -AudioMotor::AudioMotor(QObject *parent) : - QObject(parent), - m_startaudio(0), - m_writePD(NULL), - m_readPD(NULL), - m_pd_audio(NULL), - m_connectedSocket(NULL), - m_gui(true) -{ - qsrand(qrand()); - qDebug() << "Init MotorAudio"; -} - -AudioMotor::~AudioMotor() -{ - close(); -} - - -/** Init the engine - */ -bool AudioMotor::init() -{ - /* Sets the socket an connections for the comunication with PD - QFile socket(SOCKET); - if (socket.exists()) - { - socket.remove(); - }*/ - - // Socket para mandar órdenes a Pure Data - m_writePD = new QTcpSocket(this); - Q_CHECK_PTR(m_writePD); - - connect(m_writePD, SIGNAL(connected()),this, SLOT(newConexion())); - connect(m_writePD, SIGNAL(error(QAbstractSocket::SocketError)) , this, SLOT(errorWrite(QAbstractSocket::SocketError))); - - // Servidor para recibir feedback de Pure Data - m_readPD = new QTcpServer(this); - Q_CHECK_PTR(m_readPD); - connect(m_readPD, SIGNAL(newConnection()),this, SLOT(newPeer())); - - if (m_readPD->listen(QHostAddress::LocalHost, PDPORT)) { - qDebug(QString("AudioMotor| Listening to PD on TCP port %1").arg(PDPORT).toLatin1()); - } else { - qErrnoWarning(QString("AudioMotor::init() can not init TCP Server on port %1").arg(PDPORT).toLatin1()); - emit toTerminal(QString("AudioMotor::init() can not init TCP Server on port)" + PDPORT)); - } - - // Start the pd process an set up PD - m_pd_audio = new QProcess(this); - - connect(m_pd_audio, SIGNAL(readyReadStandardError()), this, SLOT(stdout())); - connect(m_pd_audio, SIGNAL(finished(int)), this, SLOT(restartAudio())); - - QString arguments; -// arguments.append("./puredata/pd -alsa -channels 2 -audiodev 1 -stderr -nostdpath -path ./puredata/externals/ -open ./puredata/lms-audio.pd "); - arguments.append("./puredata/pd -channels 2 -stderr -nostdpath -path ./puredata/externals/ -open ./puredata/lms-audio.pd "); - if (!m_gui) - arguments.append("-nogui"); - m_pd_audio->start(arguments); - if (m_pd_audio->waitForStarted(3000)){ - qDebug("AudioMotor| PD started"); - emit toTerminal(QString("AudioMotor| PD started")); - } - else - { - emit toTerminal("AudioMotor| Can not init PD"); - qErrnoWarning("AudioMotor| Can not init PD"); - return false; - } - m_startaudio++; - return true; -} - -/** Close the engine - */ -bool AudioMotor::close() -{ - disconnect(m_pd_audio, SIGNAL(readyReadStandardError()), this, SLOT(stdout())); - disconnect(m_pd_audio, SIGNAL(finished(int)), this, SLOT(restartAudio())); - m_pd_audio->terminate(); - m_pd_audio->waitForFinished(1000); - delete m_pd_audio; - m_pd_audio = NULL; - if (m_writePD != NULL) - { - disconnect(m_writePD, SIGNAL(connected()),this, SLOT(newConexion())); - m_writePD->close(); - delete m_writePD; - m_writePD = NULL; - } - if (m_readPD != NULL) - { - disconnect(m_readPD, SIGNAL(newConnection()),this, SLOT(newPeer())); - m_readPD->close(); - delete m_readPD; - m_readPD = NULL; - } -/* - QFile socket(SOCKET); - if (socket.exists()) - { - socket.remove(); - }*/ -} - -/** Set the numbers of layers - */ -void AudioMotor::setLayers (int layers){ - -} - -/** Get the number of layers -*/ -int AudioMotor::getlayers(){ - -} - -/** Load a file in memory - * - */ -bool AudioMotor::load(int layer, QString file) -{ - if (!QFile::exists(file)) - return false; - QString message = tr("%1 ").arg(layer +201); - message.append("open "); - message.append(file); - message.append(";"); - qDebug() << "AudioMotor::load " << message; - - if (QAbstractSocket::ConnectedState != m_writePD->state()) - { - qErrnoWarning("AudioMotor::load(): Socket not conected: "); - emit toTerminal("AudioMotor::load(): Socket not connectedt"); - return false; - } - if (message.size() != m_writePD->write(message.toAscii().constData(), message.size())) - { - qErrnoWarning("AudioMotor::load(): Can not write to socket"); - emit toTerminal("AudioMotor::load(): Can not write to socket"); - return false; - } - return true; -} - -/** Starts the playback at start position - * - */ -bool AudioMotor::play(int layer) -{ - QString buffer = tr("%1 %2 %3;").arg(layer).arg(PLAYBACK).arg(PLAY); - if (!sendPacket(buffer.toAscii().constData(), buffer.size())) - { - errorSending(); - return false; - } - return true; -} - -/** Unpause the playback - */ -bool AudioMotor::pause(int layer) -{ - QString buffer = tr("%1 %2 %3;").arg(layer).arg(PLAYBACK).arg(PAUSE); - if (!sendPacket(buffer.toAscii().constData(), buffer.size())) - { - errorSending(); - return false; - } - return true; -} - -/** Stops/pause the playback -* -*/ -bool AudioMotor::stop(int layer) -{ - QString buffer = tr("%1 %2 %3;").arg(layer).arg(PLAYBACK).arg(STOP); - if (!sendPacket(buffer.toAscii().constData(), buffer.size())) - { - errorSending(); - return false; - } - return true; -} - -/** Sets the start playback position - * - */ -void AudioMotor::setEntryPoint(int layer, int entry) -{ - -} - - -/** Sets the final playback position - * - */ -void AudioMotor::setExitPoint(int layer, int exit) -{ - -} - -/** Set the volumen in one layer - * @param int vol Normalized 0 to 1 - @param int layer the layer which applied - */ -void AudioMotor::setLayerVolume(int layer, float vol) -{ - QString buffer = tr("%1 %2 %3;").arg(layer).arg(VOLUME_COARSE).arg(vol); - if (!sendPacket(buffer.toAscii().constData(), buffer.size())) - { - errorSending(); - } -} - -/** Set pan in one layer - * - * @param int vol Normalized 0 (Left) to 1 (Right) - @param int layer the layer which applied - */ -void AudioMotor::setLayerPan(int layer, float pan) -{ - QString buffer = tr("%1 %2 %3;").arg(layer).arg(PAN).arg(pan); - if (!sendPacket(buffer.toAscii().constData(), buffer.size())) - { - errorSending(); - } -} - -/** Set Volumen master. - * All layers are limited by this master - * 0 will mute all the outputs - */ -void AudioMotor::setMasterVolume(int vol){ - -} - -/** Set pan master - * Will pan the master output of sound - */ -void AudioMotor::setMasterPan(int pan){ - -} - - - -/** Restart the audio process - * - */ -void AudioMotor::restartAudio() -{ - close(); - init(); -} - -/** New conexion on TCP Server - * - */ -void AudioMotor::newPeer() -{ - m_connectedSocket = m_readPD->nextPendingConnection(); - connect(m_connectedSocket, SIGNAL(readyRead()),this, SLOT(newMessage())); -} - -/** New message in a TCP socket stablished connection - * - */ - -void AudioMotor::newMessage() -{ - if (m_connectedSocket == NULL) - { - qDebug()<<("AudioMotor::newMessage() Socket not connected. Trying open it..."); - emit toTerminal("AudioMotor::newMessage() Socket not connected. Trying open it..."); - newPeer(); - return; - } - QString message = m_connectedSocket->readAll(); - parse(message); -} - -void AudioMotor::parse(QString message) -{ - QStringList list = message.split("\n", QString::SkipEmptyParts); - for (int i = 0; i < list.size(); i ++) { - if (list.at(i).size() > 0) { - qDebug() << "AudioMotor::newMessage() message received: " << list.at(i); - QChar val = list.at(i).at(0); - switch (val.digitValue()) { - case 0: - qDebug() << "AudioMotor::newMessage() Loadbang from PD Audio received"; - emit toTerminal("AudioMotor::newMessage() Loadbang from PD Audio received"); - // Conectamos a Pure Data para escribir - m_writePD->connectToHost(QHostAddress::LocalHost, SOCKET, QIODevice::WriteOnly); - if (m_writePD->waitForConnected(30000)) - emit loadbang(); - break; - case 9: - if (list.at(i).at(2).digitValue() == 0) { - if (list.at(i).at(7).isDigit() ) - emit (volChanged(list.at(i).at(4).digitValue(), ( ( list.at(i).at(6).digitValue() * 10 ) + list.at(i).at(7).digitValue() ) ) ); - else - emit (volChanged(list.at(i).at(4).digitValue(), ( ( list.at(i).at(6).digitValue() ) ) ) ); - } - break; - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - QStringList folders = list.at(i).split("/", QString::SkipEmptyParts); - if (folders.size() >= 2) - emit (mediaLoaded(val.digitValue(), folders.at(folders.size() -2), folders.at(folders.size() -1))); - break; - - } - } - } -} - -/** Error writing to PD - * - */ - -void AudioMotor::errorWrite(QAbstractSocket::SocketError error) -{ -// QString error = m_writePD->errorString(); - - qErrnoWarning(QString("AudioMotor::errorWrite() %1").arg(error).toLatin1()); - emit toTerminal(QString("AudioMotor::errorWrite() ") + error); -} - -/** Sends packets to Pure Data audio -* -*/ - -bool AudioMotor::sendPacket(const char *buffer, int bufferLen) -{ - if (m_writePD == NULL) { - return false; - } - if (QAbstractSocket::ConnectedState != m_writePD->state()) - { - return false; - } - if (bufferLen != m_writePD->write((const char*)buffer, bufferLen)) - { - return false; - } - return true; -} - -// Function error sending packets to PD audio -void AudioMotor::errorSending() { - qDebug() << "AudioMotor| Can not send packets to PD"; - emit toTerminal("AudioMotor| Can not send packets to PD"); -} - diff --git a/src/audiomotor.h b/src/audiomotor.h deleted file mode 100644 index 714ee31..0000000 --- a/src/audiomotor.h +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef AUDIOMOTOR_H -#define AUDIOMOTOR_H - -#include -#include -#include -#include -#include -#include -#include - -#include "dmxPersonality.h" - -#define PDPORT 9198 -#define SOCKET 9197 // "/tmp/socket" - -class AudioMotor : public QObject -{ - Q_OBJECT - -public: - - static AudioMotor *getInstance(); - - /** Init the engine - */ - bool init(); - - /** Close the engine - */ - bool close(); - - /** Set the numbers of layers - */ - void setLayers (int layers); - - /** Get the number of layers - */ - int getlayers(); - - /** Load a file in memory - * - */ - bool load(int layer, QString file); - - /** Starts the playback at start position - * - */ - bool play(int layer); - - /** Pause/unpause the playback - */ - bool pause(int layer); - - /** Stops the playback and reset the media to the start position - * - */ - bool stop(int layer); - - /** Sets the start playback position - * - */ - void setEntryPoint(int layer, int entry); - - /** Sets the final playback position - * - */ - void setExitPoint(int layer, int exit); - - /** Set the volumen in one layer - * - */ - void setLayerVolume(int layer, float vol); - - /** Set pan in one layer - */ - void setLayerPan(int layer, float pan); - - /** Set Volumen master. - * All layers are limited by this master - * 0 will mute all the outputs - */ - void setMasterVolume(int vol); - - /** Set pan master - * Will pan the master output of sound - */ - void setMasterPan(int pan); - - inline void setGui(bool gui) { m_gui = gui; } - -private: - - AudioMotor(QObject *parent = 0); - virtual ~AudioMotor(); - static AudioMotor *_instance; - - bool m_gui; - int m_layersNumber; - QProcess *m_pd_audio; // Pure Data process for audio - - // Audio TCP Sockets - QTcpSocket *m_writePD; - QTcpServer *m_readPD; - QTcpSocket *m_connectedSocket; // connected socket to server - int m_startaudio; // Counter starts audio engine. Debugging purpose - - bool sendPacket(const char *buffer, int bufferLen); - void errorSending(); - void parse(QString message); - -signals: - void loadbang(); - void newConnection(); - void mediaLoaded(int layer, QString folder, QString file); - void volChanged(int layer, int vol); - void toTerminal(QString message); - -private slots: - - void newPeer(); - inline void newConexion() { qDebug() << "AudioMotor write socket connected to PD"; } - void restartAudio(); - void newMessage(); - void errorWrite(QAbstractSocket::SocketError error); - /** - * - * Listen the terminal exit of PD - * - */ - - // Sacamos la salida de Pure Data Audio en la terminal - inline void stdout() { - QString out = m_pd_audio->readAllStandardError(); - out.chop(1); - if (!out.isEmpty()) - { - qDebug() << "AudioMotor from PD: " << out; - } - } -}; - -#endif // AUDIOMOTOR_H diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 9631652..05004e2 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -12,20 +12,34 @@ AudioWidget::AudioWidget(QWidget *parent) : qDebug( "Init AudioWidget"); } -void AudioWidget::mediaLoaded(int layer, QString folder, QString file) +void AudioWidget::mediaLoaded(int layer, QString media) { - QLayoutItem * const item = layout->itemAt(layer - 1); - qDebug() << "AudioWidget::mediaLoaded Received layer: " << layer - << "Folder: " << folder - <<"File : " << file; - dynamic_cast(item->widget())->setFolder(folder); - dynamic_cast(item->widget())->setFile(file); + QLayoutItem * const item = layout->itemAt(layer); + dynamic_cast(item->widget())->loadMedia(media); +/* qDebug() << "AudioWidget::mediaLoaded Received layer: " << layer + << "Media: " << media; */ } -void AudioWidget::volChanged(int layer, int vol) { - QLayoutItem * const item = layout->itemAt(layer - 1); - qDebug() << "AudioWidget::volChanged Received layer: " << layer - << "Vol : " << vol; +void AudioWidget::volChanged(int layer, qreal vol) { + QLayoutItem * const item = layout->itemAt(layer); +/* qDebug() << "AudioWidget::volChanged Received layer: " << layer + << "Vol : " << vol; */ dynamic_cast(item->widget())->setVol(vol); } +void AudioWidget::playbackChanged(int layer, QAudio::State state) +{ + QLayoutItem * const item = layout->itemAt(layer); + switch (state) { + case QAudio::ActiveState: + dynamic_cast(item->widget())->play(); + break; + case QAudio::StoppedState: + dynamic_cast(item->widget())->stop(); + break; + case QAudio::SuspendedState: + dynamic_cast(item->widget())->toggleSuspendResume(); + case QAudio::IdleState: + break; + } +} diff --git a/src/audiowidget.h b/src/audiowidget.h index 6b39808..6ac58b5 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -10,25 +10,20 @@ class AudioWidget : public QWidget { - Q_OBJECT public: - AudioWidget(QWidget *parent); private: - -// QList *list; - QHBoxLayout *layout; signals: public slots: - void mediaLoaded(int layer, QString folder, QString file); - void volChanged(int layer, int vol); - + void mediaLoaded(int layer, QString media); + void volChanged(int layer, qreal vol); + void playbackChanged(int layer, QAudio::State state); }; #endif // AUDIOWIDGET_H diff --git a/src/defines.h b/src/defines.h index 9e221b9..df8f3a4 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,8 +1,57 @@ #ifndef DEFINES_H #define DEFINES_H + +#define VERSION "LibreMediaServer-Audio 0.1.0" +#define COPYRIGHT "(C) 2014 Santi Norena libremediaserver@gmail.com" +#define LICENSE "GPL 3 License. See LICENSE.txt and credits.txt for details" + #define LAYERS_NUMBER 4 +#define DEFAULT_FILE "lms-audio.xlm" + +const int DurationSeconds = 1; +const int ToneSampleRateHz = 600; +const int DataSampleRateHz = 44100; +const int BufferSize = 32768; + +#define SUSPEND_LABEL "Suspend playback" +#define RESUME_LABEL "Resume playback" +#define PLAY_LABEL "Playing" +#define STOP_LABEL "Stopped" +#define PAUSE_LABEL "Pause" +#define IDDLE_LABEL "Iddle playback" +#define VOLUME_LABEL "Volume:" +#define PROGRESS_LABEL "Progress: " +#define PROGRESS_TIME_LABEL "Current Time: " +#define REMAINING_TIME "Remaining Time: " +#define TOTAL_TIME_LABEL "Track Time: " +#define FILE_LABEL "File: " +#define FOLDER_LABEL "Folder: " +#define STATUS_LABEL "Status: " + +// struct where save the DMX settings for each layer +struct dmxSetting { + int address; + uint universe; + bool updated; + int layer; +}; + +// Media Information for MELIn packages. v1.0 +struct MediaFile { + quint8 Number; // 0-based contiguous index of the media. + QString MediaName;// Media name. + quint32 MediaLength;// Media length (in frames). +}; + +// Media Library for ELin packages v1.0 +struct MediaFolder { + quint8 m_Id; // Library id. + QString m_Name;// Library name. + quint8 m_ElementCount;// Number of elements in the library. + QList m_MediaInformation; // Pointer to the Medias Information List of this Library +}; #endif // DEFINES_H diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index c73288d..d4ed8cf 100755 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -26,25 +26,28 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) : QMainWindow(parent) { qDebug() << "********************************************************************************"; - qDebug() << QDate::currentDate() << QTime::currentTime(); + qDebug() << QDate::currentDate().toString() << " "<< QTime::currentTime().toString(); qDebug() << VERSION; qDebug() << COPYRIGHT; qDebug() << LICENSE; - // Inicia el User Interface ui.setupUi(this); + this->setWindowTitle(VERSION); // Inicia el widget Terminal textEdit = new QTextEdit(this); - textEdit->append(QString::fromAscii(VERSION)); - textEdit->append(QString::fromAscii(LICENSE)); - textEdit->append(QString::fromAscii(COPYRIGHT)); - + textEdit->append(QString::fromLatin1(VERSION)); + textEdit->append(QString::fromLatin1(LICENSE)); + textEdit->append(QString::fromLatin1(COPYRIGHT)); QDockWidget *bottomWidget = new QDockWidget(tr("Terminal"), this); bottomWidget->setAllowedAreas(Qt::BottomDockWidgetArea); bottomWidget->setWidget(textEdit); addDockWidget(Qt::BottomDockWidgetArea, bottomWidget); + // Inicia el widget central de audio + aw = new AudioWidget(this); + setCentralWidget(aw); + // Inicia la lectura de dmx a través de ola ola = new olaThread(); Q_CHECK_PTR(ola); @@ -53,11 +56,6 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) ola->start(QThread::TimeCriticalPriority ); ola->blockSignals(true); - // Inicia el widget central de audio - - aw = new AudioWidget(this); - setCentralWidget(aw); - // Inicia el widget Master. No implementado todavía /* amw = new AudioMasterWidget(this); @@ -66,17 +64,6 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) topWidget->setWidget(amw); addDockWidget(Qt::TopDockWidgetArea, topWidget); */ - // Parse the command line options - if (args.contains("-gui")) - { - qDebug()<< "libremediaserver Constructor option GUI detected"; - AudioMotor::getInstance()->setGui(true); - textEdit->append("Pure Data GUI's will be shown"); - } else { AudioMotor::getInstance()->setGui(false); } - - connect(AudioMotor::getInstance(), SIGNAL(toTerminal(QString)), - this, SLOT(toTerminal(QString))); - if (args.contains("-log")) { textEdit->append("Log to file"); @@ -90,30 +77,18 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) Settings *set = new Settings(); - // Iniciamos Pure Data - AudioMotor::getInstance()->init(); - connect(set, SIGNAL( layersNumber(int)), ola, SLOT( setLayersNumber(int))); - connect(set, SIGNAL( DMXConf(dmxSetting ) ), ola, SLOT( setDMXConf(dmxSetting) ) ); - connect(AudioMotor::getInstance(), SIGNAL(mediaLoaded(int, QString, QString)), - aw, SLOT(mediaLoaded(int, QString, QString))); - - connect(AudioMotor::getInstance(), SIGNAL(volChanged(int, int)), - aw, SLOT(volChanged(int, int))); - - connect(AudioMotor::getInstance(), SIGNAL(loadbang()), - this, SLOT (loadbang())); - connect(ola, SIGNAL( dmxOutput(int, int, int) ), this, SLOT( dmxInput(int, int, int) ) ); // Lee la configuración por defecto set->readDefaultFile(); ola->blockSignals(false); + ola->resendDmx(); } /////////////////////////////////////////////////////////////////// @@ -125,9 +100,7 @@ libreMediaServerAudio::~libreMediaServerAudio() // save_finish(); delete MediaLibrary::getInstance(); ola->stop(); - AudioMotor::getInstance()->close(); -// qDebug() << "PD Audio restarts: " << m_startaudio; - qDebug() << QDate::currentDate() << QTime::currentTime(); + qDebug() << QDate::currentDate().toString() << " " << QTime::currentTime().toString(); qDebug() << "********************************************************************************"; return; } @@ -171,7 +144,7 @@ void libreMediaServerAudio::ChangeMediaPath() QString file = fileNames.at(0); MediaLibrary::getInstance()->setPath(file); QString desc = tr("Media Path Changed to: %1").arg(m_pathmedia); - textEdit->append(desc.toAscii()); + textEdit->append(desc); } @@ -195,47 +168,45 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) // qDebug() << tr("olaInterface|") << "newdmx layer" << layer << "channel" << channel << "value" << value; QString mediaFile = NULL; int aux; - float f; + qreal f; switch(channel){ case DMX_FOLDER:// Folder aux = ola->getValue(layer, DMX_FILE); mediaFile = MediaLibrary::getInstance()->requestNewFile(value, aux); if (QFile::exists(mediaFile)) - AudioMotor::getInstance()->load(layer, mediaFile); + aw->mediaLoaded(layer, mediaFile); break; case DMX_FILE:// File aux = ola->getValue(layer, DMX_FOLDER); mediaFile = MediaLibrary::getInstance()->requestNewFile(aux, value); if (QFile::exists(mediaFile)) - AudioMotor::getInstance()->load(layer, mediaFile); + aw->mediaLoaded(layer, mediaFile); break; case VOLUME_COARSE: f = ( value * 0x100 ) + ola->getValue(layer, VOLUME_FINE); - AudioMotor::getInstance()->setLayerVolume(layer, f/65535); + aw->volChanged(layer, f / 65535); break; case VOLUME_FINE: f = ( ola->getValue(layer, VOLUME_COARSE) * 0x100 ) + value; - AudioMotor::getInstance()->setLayerVolume(layer, f/65535); + aw->volChanged(layer, f / 655.35); break; case PAN: f = (float)value / 255; - AudioMotor::getInstance()->setLayerPan(layer, f ); break; case PLAYBACK: aux = value / 25; switch (aux) { case 0 : - AudioMotor::getInstance()->play(layer); + aw->playbackChanged(layer, QAudio::ActiveState); break; case 1 : - AudioMotor::getInstance()->stop(layer); + aw->playbackChanged(layer, QAudio::StoppedState); break; case 2 : - AudioMotor::getInstance()->pause(layer); + aw->playbackChanged(layer, QAudio::SuspendedState); break; } default: -// emit dmxInput(layer, channel, value); break; } } diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index caf2fe2..a9973d2 100755 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -20,11 +20,7 @@ #define LIBREMEDIASERVER_H #include -//#include -#include -#include -#include - +#include #include #include #include @@ -35,20 +31,14 @@ #include #include "settings.h" -#include "audiomotor.h" #include "olathread.h" -#include "audiolayerwidget.h" #include "audiomasterwidget.h" #include "audiowidget.h" +#include "defines.h" #include "ui_libremediaserver-audio.h" -#define VERSION "LibreMediaServer-Audio Version 0.1.0" -#define COPYRIGHT "(C) 2014 Santi Norena libremediaserver@gmail.com" -#define LICENSE "GPL 3 License. See LICENSE.txt and credits.txt for details" - class QMenu; -//class QProcess; class libreMediaServerAudio : public QMainWindow { @@ -64,25 +54,18 @@ public: protected: QString m_pathmedia; // Path to Medias - QProcess *m_ola; // OLA daemon process - - bool m_gui; private: QTextEdit *textEdit; // Terminal de feedback AudioMasterWidget *amw; -// AudioLayerWidget *alw; AudioWidget *aw; - olaThread *ola; - void open_start(); - void save_finish(); - void open(QFile *file); - void save(QFile *file); - -// void MessageHandler(QtMsgType type, const char *msg); + void open_start(); + void save_finish(); + void open(QFile *file); + void save(QFile *file); public slots: @@ -98,16 +81,13 @@ private slots: */ void loadbang(); - void olasetup(); - void dmxInput(int layer, int channel, int value); // Menu File void openFile(); void saveFile(); void ChangeMediaPath();// Change the path to medias - }; #endif // LIBREMEDIASERVER_H diff --git a/src/libremediaserver-audio.pro b/src/libremediaserver-audio.pro index a455f01..d28775f 100755 --- a/src/libremediaserver-audio.pro +++ b/src/libremediaserver-audio.pro @@ -1,29 +1,31 @@ TEMPLATE = app TARGET = libremediaserver-audio -QT += network script webkit + +QT += network script webkitwidgets widgets multimedia + CONFIG += debug DESTDIR = ./debug HEADERS += libremediaserver-audio.h \ medialibrary.h \ - audiomotor.h \ olathread.h \ audiolayerwidget.h \ dmxPersonality.h \ audiowidget.h \ audiomasterwidget.h \ defines.h \ - settings.h + settings.h \ + audiodecoder.h SOURCES += main.cpp \ libremediaserver-audio.cpp \ medialibrary.cpp \ - audiomotor.cpp \ olathread.cpp \ audiolayerwidget.cpp \ audiowidget.cpp \ audiomasterwidget.cpp \ - settings.cpp + settings.cpp \ + audiodecoder.cpp FORMS += \ libremediaserver-audio.ui diff --git a/src/main.cpp b/src/main.cpp index 2e76bc9..e95dd0c 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,35 +22,36 @@ #include "libremediaserver-audio.h" // Handler for pipe the stderr to a log file - bool initMessageHandler = false; QFile outFile; -void MessageHandler(QtMsgType type, const char *msg) +void MessageHandler(QtMsgType type, const QMessageLogContext &logcontext, const QString &msg) { + Q_UNUSED(logcontext); QString txt; switch (type) { case QtDebugMsg: - txt = QString("Debug: %1").arg(msg); + txt.append("Debug: "); + txt.append(msg); break; case QtWarningMsg: - txt = QString("Warning: %1").arg(msg); + txt.append("Warning: "); + txt.append(msg); break; case QtCriticalMsg: - txt = QString("Critical: %1").arg(msg); + txt.append("Critical: "); + txt.append(msg); break; case QtFatalMsg: - txt = QString("Fatal: %1").arg(msg); + txt.append("Fatal: "); + txt.append(msg); abort(); } // Create the log dir and log file - if (!initMessageHandler) - { + if (!initMessageHandler) { QDir dir; - if (!dir.exists("log")) - { - if (!dir.mkdir("log")) - { + if (!dir.exists("log")) { + if (!dir.mkdir("log")) { qDebug()<<"MessageHandler: Can not create log folder"; return; } @@ -62,9 +63,8 @@ void MessageHandler(QtMsgType type, const char *msg) filename.append(date.toString("ddMMyy-")); filename.append(time.toString("hhmmss.txt")); outFile.setFileName(filename); - if (!outFile.open(QIODevice::WriteOnly | QIODevice::Append)) - { - qDebug()<<"main/MessageHandler/Qfile::open: can not open log file"; + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Append)) { + qWarning("main/MessageHandler/Qfile::open: can not open log file"); return; } initMessageHandler = true; @@ -76,42 +76,31 @@ void MessageHandler(QtMsgType type, const char *msg) int main(int argc, char *argv[]) { QApplication app(argc, argv); - QStringList args = app.arguments(); // parse the command line if (args.size() > 1) { - if (args.contains("-v") > 0) + if (args.contains("-v")) { qDebug() << VERSION; qDebug() << COPYRIGHT; qDebug() << LICENSE; return 0; } - if (args.contains("-h") > 0) + if (args.contains("-h")) { qDebug() << VERSION; qDebug() << COPYRIGHT; qDebug() << LICENSE; qDebug() << "Help for command line options:"; qDebug() << "-v show the version and exits"; - qDebug() << "-gui show the Pure Data GUI's"; qDebug() << "-log write the debug information to a log file instead stderr"; qDebug() << "-h this help"; return 0; } if (args.contains("-log")) { - qInstallMsgHandler(MessageHandler); - } - if (!args.contains("-gui")) - { - for (int i=1; i MediaLibrary *MediaLibrary::_instance = 0; @@ -26,8 +27,7 @@ MediaLibrary::MediaLibrary(QObject *parent) : void MediaLibrary::initMediaLibrary() { QDir dir; if (!dir.cd(m_pathmedia)) { - qWarning("olaInterface::initMediaLibrary| Can not cd to the path: "); - qWarning(m_pathmedia.toAscii().constData()); + qWarning() << "olaInterface::initMediaLibrary| Can not cd to the path: " << m_pathmedia.toLatin1().constData(); return; } m_media = new QList; @@ -88,5 +88,3 @@ QString MediaLibrary::requestNewFile(int folder, int file){ } return newfile; } - - diff --git a/src/medialibrary.h b/src/medialibrary.h index e0566b0..46c045c 100644 --- a/src/medialibrary.h +++ b/src/medialibrary.h @@ -4,21 +4,7 @@ #include #include -// Media Information for MELIn packages. v1.0 -struct MediaFile { - quint8 Number; // 0-based contiguous index of the media. - QString MediaName;// Media name. - quint32 MediaLength;// Media length (in frames). -}; - -// Media Library for ELin packages v1.0 -struct MediaFolder { - quint8 m_Id; // Library id. - QString m_Name;// Library name. - quint8 m_ElementCount;// Number of elements in the library. - QList m_MediaInformation; // Pointer to the Medias Information List of this Library -}; - +#include "defines.h" class MediaLibrary : public QObject { @@ -28,7 +14,6 @@ public: static MediaLibrary *getInstance(); inline void setPath(QString path) { m_pathmedia = path; rescanMediaLibrary();} - QString requestNewFile(int folder, int layer); private: @@ -36,13 +21,11 @@ private: explicit MediaLibrary(QObject *parent = 0); static MediaLibrary *_instance; inline QString getPath () { return m_pathmedia; } // Get the path to the medias folder tree. - inline void deleteMediaLibrary() { delete m_media; m_media = NULL; } /** * Change library/path */ - inline void rescanMediaLibrary() - { + inline void rescanMediaLibrary() { deleteMediaLibrary(); initMediaLibrary(); } @@ -54,8 +37,6 @@ private: /** Called when there is a change in the channels folder or file * this is called. Creates a new source. */ - - QList getMediaInformation(QDir dir); // Get all the information of each media file in a dir signals: diff --git a/src/olathread.cpp b/src/olathread.cpp index 42cb448..b19bef2 100644 --- a/src/olathread.cpp +++ b/src/olathread.cpp @@ -1,8 +1,6 @@ #include "olathread.h" - olaThread::olaThread(QObject *parent) -// : QObject(parent) { m_universe = new QList(); m_counter = 0; @@ -21,11 +19,6 @@ olaThread::olaThread(QObject *parent) m_dmx[i][j] = 0; } } -/* int argc = 1; - char argv[] = "-l 3"; - if (!ola::AppInit (&argc, &argv,"LMS", "DMX sound media server")) - qCritical("Can not init ola");*/ - // set up ola connection m_clientWrapper = new ola::client::OlaClientWrapper; Q_CHECK_PTR(m_clientWrapper); @@ -45,12 +38,8 @@ olaThread::~olaThread() { /** Open the connection with olad and start processing data. */ -void olaThread::run() { - - /* register the universe - if (!m_client->RegisterUniverse(m_universe, ola::REGISTER,ola::NewSingleCallback(&RegisterComplete))) { - qDebug() << "Can not register universe %1".arg(m_universe); - }*/ +void olaThread::run() +{ qDebug() << tr("olaThread| Running"); emit toTerminal("Reading DMX"); m_clientWrapper->GetSelectServer()->Run(); @@ -77,7 +66,6 @@ void olaThread::stop() */ // ToDo: It can be more efficient making the dmx buffer a DmxBuffer class instead a int array and compare with the new if there is changes at start. Also all access to the buffer it should be get/set. I should profile this - // typedef Callback2 ola::client::RepeatableDMXCallback void olaThread::NewDmx(const ola::client::DMXMetadata &data, const ola::DmxBuffer &buffer) @@ -88,7 +76,7 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, for (int i = 0; i < m_layersNumber; i++) { // loop for reading the channels by layer. if((m_settings.at(i).universe == universe) && ( m_settings.at(i).address > -1 )) { // Compare if the layer is from this universe - // AND if the DMX address is 0 or greater, process this layer. + // AND if the DMX address is 0 or greater, process this layer. for (int j = 0; j < LAYER_CHANNELS; j++){ int value = buffer.Get((m_settings.at(i).address) + j); // Get the value for this channel. if (m_dmx[i][j] != value) { // Compare the new value with the old value. diff --git a/src/olathread.h b/src/olathread.h index ae44238..c91998f 100644 --- a/src/olathread.h +++ b/src/olathread.h @@ -17,14 +17,6 @@ #include "defines.h" #include "dmxPersonality.h" -// struct where save the DMX settings for each layer -struct dmxSetting { - int address; - uint universe; - bool updated; - int layer; -}; - class olaThread : public QThread { Q_OBJECT @@ -53,24 +45,18 @@ public: private: void run (); - ola::client::OlaClientWrapper *m_clientWrapper; ola::client::OlaClient *m_client; unsigned int m_counter; struct timeval m_last_data; // Last DMX frame received // DMX Conf -// Cambiar para múltiples universos. Array? método de de ola::client? QList *m_universe; // Registered universes. int m_layersNumber; // Number of layers in wich divide the dmx frame. Each layer, one source. - int m_dmx[LAYERS_NUMBER][LAYER_CHANNELS]; // DMX Buffer. Habría que cambiarlo si queremos hacer las capas dinámicas - QList m_settings; - inline void registerUniverse(int universe) - { - + inline void registerUniverse(int universe) { // void ola::client::OlaClient::RegisterUniverse(unsigned int universe,RegisterAction register_action,SetCallback * callback m_client->RegisterUniverse(universe, ola::client::REGISTER,ola::NewSingleCallback(this, &olaThread::RegisterComplete)); } @@ -78,9 +64,7 @@ private: /** * Control de errores en el registro de Universos en OLA */ - // typedef SingleUseCallback1 ola::client::SetCallback - inline void RegisterComplete(const ola::client::Result &error) { if (error.Success()) { qDebug() << "Register Universe success"; @@ -100,13 +84,9 @@ public slots: void stop(); // Close the connection with olad. void setLayersNumber(int layersNumber); - - inline void setDMXConf(dmxSetting set) - { + inline void setDMXConf(dmxSetting set) { if (set.layer >= m_layersNumber) { return; } - m_settings.replace(set.layer, set); - // ToDo: registro del nuevo universo si no está registrado ya if (!m_universe->contains(set.universe)) { registerUniverse(set.universe); diff --git a/src/settings.cpp b/src/settings.cpp index 2bfcbb8..85a2f49 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -11,8 +11,6 @@ Settings::Settings(QObject *parent) : // - The number of sources/layers controlled by DMX // - The first DMX channel of each source/layer // - The universe to bind in OLA -// All this is being moved to settingsDialog Class - void Settings::readFromFile(QString file) { QFile* xmlFile = new QFile(file); @@ -43,9 +41,6 @@ void Settings::readFromFile(QString file) { continue; } } -/* if (worker->m_layersNumber > MAX_SOURCE_DMX) { - worker->m_layersNumber = MAX_SOURCE_DMX; - }*/ QString add = "layer"; add.append(QString("%1").arg(counter)); if((xmlReader->name() == add)) { @@ -54,10 +49,6 @@ void Settings::readFromFile(QString file) { temp.universe = xmlReader->attributes().value("universe").toLocal8Bit().toInt(); temp.layer = counter; emit DMXConf(temp); - // If the universe is not in the list, append it. -// if(!worker->m_universe.contains(temp.universe)) { -// worker->m_universe.append(temp.universe); -// } } counter++; } diff --git a/src/settings.h b/src/settings.h index 741d1ca..fd4a489 100644 --- a/src/settings.h +++ b/src/settings.h @@ -7,10 +7,8 @@ #include #include "olathread.h" -#include "audiomotor.h" #include "medialibrary.h" - -#define DEFAULT_FILE "lms-audio.xlm" +#include "defines.h" class Settings : public QObject { From a6909f8c1665dff4795cf4f0fdb25f4f207507d0 Mon Sep 17 00:00:00 2001 From: santi Date: Thu, 10 Jul 2014 17:37:52 +0200 Subject: [PATCH 2/3] Debug messages to GUI terminal --- src/audiolayerwidget.cpp | 21 ++++--- src/defines.h | 4 +- src/libremediaserver-audio.cpp | 57 +++++++++++-------- src/libremediaserver-audio.h | 10 ++-- src/libremediaserver-audio.pro | 4 +- src/main.cpp | 101 +++++++++++++++++++++++---------- src/olathread.cpp | 14 ++--- 7 files changed, 131 insertions(+), 80 deletions(-) diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index 177dc33..eece14d 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -7,13 +7,14 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): QGroupBox(parent) - , m_pullTimer(new QTimer(this)) , m_suspendResumeButton(0) , m_deviceBox(0) - , m_output(0) + , m_pullTimer(new QTimer(this)) , m_device(QAudioDeviceInfo::defaultOutputDevice()) , m_audioOutput(0) + , m_output(0) , m_buffer(BufferSize, 0) + , m_decoder(0) { this->setTitle(name); @@ -110,6 +111,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): 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()), @@ -128,7 +130,8 @@ void AudioLayerWidget::createAudioOutput() m_audioOutput->setNotifyInterval(100); connect(m_audioOutput, SIGNAL(notify()), SLOT(notified())); connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State))); - m_decoder->start(); + m_output = m_audioOutput->start(); + toggleSuspendResume(); } void AudioLayerWidget::deviceChanged(int index) @@ -185,8 +188,8 @@ void AudioLayerWidget::pullTimerExpired() while (chunks) { const qint64 len = m_decoder->read(m_buffer.data(), m_audioOutput->periodSize()); if ( len == -1) { - qDebug() << "End of file"; stop(); + qDebug()<< this->title() << " End of file"; break; } if (len) { @@ -250,9 +253,9 @@ void AudioLayerWidget::fileLoaded(QString file) void AudioLayerWidget::play() { - m_decoder->setPos(0); - m_output = m_audioOutput->start(); +// m_decoder->setPos(0); m_pullTimer->start(20); + m_audioOutput->resume(); m_statusValue->setText(PLAY_LABEL); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } @@ -264,10 +267,10 @@ void AudioLayerWidget::pause() void AudioLayerWidget::stop() { - m_pullTimer->stop(); m_audioOutput->suspend(); - m_audioOutput->reset(); - m_decoder->setPos(0); + m_pullTimer->stop(); +// m_audioOutput->reset(); + m_decoder->setPos(44); m_statusValue->setText(STOP_LABEL); m_suspendResumeButton->setText(tr(RESUME_LABEL)); } diff --git a/src/defines.h b/src/defines.h index df8f3a4..d46e749 100644 --- a/src/defines.h +++ b/src/defines.h @@ -2,7 +2,7 @@ #define DEFINES_H -#define VERSION "LibreMediaServer-Audio 0.1.0" +#define VERSION "Libre Media Server Audio 0.1.0" #define COPYRIGHT "(C) 2014 Santi Norena libremediaserver@gmail.com" #define LICENSE "GPL 3 License. See LICENSE.txt and credits.txt for details" @@ -25,7 +25,7 @@ const int BufferSize = 32768; #define PROGRESS_LABEL "Progress: " #define PROGRESS_TIME_LABEL "Current Time: " #define REMAINING_TIME "Remaining Time: " -#define TOTAL_TIME_LABEL "Track Time: " +#define TOTAL_TIME_LABEL "Total Time: " #define FILE_LABEL "File: " #define FOLDER_LABEL "Folder: " #define STATUS_LABEL "Status: " diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index d4ed8cf..9c6ba60 100755 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -18,6 +18,11 @@ */ #include "libremediaserver-audio.h" +QTextEdit * libreMediaServerAudio::textEdit = 0; + + + + /** / Constructor */ @@ -25,25 +30,24 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) : QMainWindow(parent) { - qDebug() << "********************************************************************************"; - qDebug() << QDate::currentDate().toString() << " "<< QTime::currentTime().toString(); - qDebug() << VERSION; - qDebug() << COPYRIGHT; - qDebug() << LICENSE; - + Q_UNUSED(args); ui.setupUi(this); - this->setWindowTitle(VERSION); // Inicia el widget Terminal - textEdit = new QTextEdit(this); - textEdit->append(QString::fromLatin1(VERSION)); - textEdit->append(QString::fromLatin1(LICENSE)); - textEdit->append(QString::fromLatin1(COPYRIGHT)); + textEdit = new QTextEdit; + textEdit->setReadOnly(true); QDockWidget *bottomWidget = new QDockWidget(tr("Terminal"), this); bottomWidget->setAllowedAreas(Qt::BottomDockWidgetArea); bottomWidget->setWidget(textEdit); addDockWidget(Qt::BottomDockWidgetArea, bottomWidget); + this->setWindowTitle(VERSION); + +// qDebug() << QDate::currentDate().toString() << " "<< QTime::currentTime().toString(); + qDebug() << VERSION; + qDebug() << COPYRIGHT; + qDebug() << LICENSE; + // Inicia el widget central de audio aw = new AudioWidget(this); setCentralWidget(aw); @@ -51,11 +55,19 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) // Inicia la lectura de dmx a través de ola ola = new olaThread(); Q_CHECK_PTR(ola); - connect(ola, SIGNAL(toTerminal(QString)), - this, SLOT(toTerminal(QString))); - ola->start(QThread::TimeCriticalPriority ); - ola->blockSignals(true); + connect(ola, SIGNAL(toTerminal(QString)), + textEdit, SLOT(append(QString)), Qt::QueuedConnection); + + ola->blockSignals(true); + ola->start(QThread::TimeCriticalPriority ); + +/* connect(MediaLibrary::getInstance(), SIGNAL(debug(QString)), + textEdit, SLOT(append(QString)), Qt::QueuedConnection); + + connect(MediaLibrary::getInstance(), SIGNAL(warning(QString)), + textEdit, SLOT(append(QString)), Qt::QueuedConnection); +*/ // Inicia el widget Master. No implementado todavía /* amw = new AudioMasterWidget(this); @@ -64,11 +76,6 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) topWidget->setWidget(amw); addDockWidget(Qt::TopDockWidgetArea, topWidget); */ - if (args.contains("-log")) - { - textEdit->append("Log to file"); - } - // Conectamos los menus connect(ui.actionOpen_conf, SIGNAL(triggered()), this, SLOT(openFile())); connect(ui.actionSave_conf, SIGNAL(triggered()), this, SLOT(saveFile())); @@ -98,11 +105,11 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) libreMediaServerAudio::~libreMediaServerAudio() { // save_finish(); - delete MediaLibrary::getInstance(); - ola->stop(); - qDebug() << QDate::currentDate().toString() << " " << QTime::currentTime().toString(); - qDebug() << "********************************************************************************"; - return; +// delete MediaLibrary::getInstance(); +// ola->stop(); +// qDebug() << QDate::currentDate().toString() << " " << QTime::currentTime().toString(); +// qDebug() << "********************************************************************************"; +// return; } /////////////////////////////////////////////////////////////////// diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index a9973d2..a05e21b 100755 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -51,13 +51,15 @@ public: Ui::LibreMediaServerAudio ui; + static QTextEdit *textEdit; // Terminal de feedback + protected: QString m_pathmedia; // Path to Medias private: - QTextEdit *textEdit; // Terminal de feedback +// void MessageHandler(QtMsgType type, const QMessageLogContext &logcontext, const QString &msg); AudioMasterWidget *amw; AudioWidget *aw; olaThread *ola; @@ -68,11 +70,7 @@ private: void save(QFile *file); public slots: - - inline void toTerminal (QString message) - { - textEdit->append(message); - } +// inline void toTerminal(QString msg) { textEdit->append(msg); } private slots: diff --git a/src/libremediaserver-audio.pro b/src/libremediaserver-audio.pro index d28775f..037f010 100755 --- a/src/libremediaserver-audio.pro +++ b/src/libremediaserver-audio.pro @@ -32,7 +32,8 @@ FORMS += \ #INCLUDEPATH += ./ -LIBS += -L./debug -lola -lolacommon +LIBS += -lola -lolacommon +# -L./debug #win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../debug/release/ -lcitp #else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../debug/debug/ -lcitp @@ -43,7 +44,6 @@ LIBS += -L./debug -lola -lolacommon RESOURCES = - OTHER_FILES += \ ../LICENSE.txt \ ../instalacion.txt \ diff --git a/src/main.cpp b/src/main.cpp index e95dd0c..d00acb0 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,36 +19,22 @@ */ #include +#include +#include #include "libremediaserver-audio.h" -// Handler for pipe the stderr to a log file +// Handler for pipe the stderr to a log file and texEdit bool initMessageHandler = false; QFile outFile; +QMutex mutex; +//QMutexLocker mutexLocker(mutex); -void MessageHandler(QtMsgType type, const QMessageLogContext &logcontext, const QString &msg) +void MessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - Q_UNUSED(logcontext); - QString txt; - switch (type) { - case QtDebugMsg: - txt.append("Debug: "); - txt.append(msg); - break; - case QtWarningMsg: - txt.append("Warning: "); - txt.append(msg); - break; - case QtCriticalMsg: - txt.append("Critical: "); - txt.append(msg); - break; - case QtFatalMsg: - txt.append("Fatal: "); - txt.append(msg); - abort(); - } + Q_UNUSED(context); +// mutex.lock(); // Create the log dir and log file - if (!initMessageHandler) { +/* if (!initMessageHandler) { QDir dir; if (!dir.exists("log")) { if (!dir.mkdir("log")) { @@ -69,13 +55,74 @@ void MessageHandler(QtMsgType type, const QMessageLogContext &logcontext, const } initMessageHandler = true; } - QTextStream ts(&outFile); - ts << txt << endl; + QTextStream ts(&outFile);*/ + if (libreMediaServerAudio::textEdit == 0) + { + QByteArray localMsg = msg.toLocal8Bit(); + switch (type) { + case QtDebugMsg: + fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtWarningMsg: + fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtCriticalMsg: + fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + break; + case QtFatalMsg: + fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); + abort(); + } + } + else + { + QString txt; + switch (type) { + case QtDebugMsg: + txt.append("Debug: "); + txt.append(context.file); + txt.append(context.line); + txt.append(context.function); + txt.append(msg); +// libreMediaServerAudio::textEdit->append(msg);, context.file, context.line, context.function); + libreMediaServerAudio::textEdit->append(txt); + break; + case QtWarningMsg: +// txt.append("Warning: "); +// txt.append(msg); + libreMediaServerAudio::textEdit->append(msg); + break; + case QtCriticalMsg: +// txt.append("Critical: "); +// txt.append(msg); + libreMediaServerAudio::textEdit->append(msg); + break; + case QtFatalMsg: +// txt.append("Fatal: "); +// txt.append(msg); + libreMediaServerAudio::textEdit->append(msg); +// ts << txt << endl; + abort(); + } +// outFile.write(txt.toLatin1().constData(), txt.size()); +// ts << txt << endl; +// libreMediaServerAudio::textEdit->append(txt); + } +// mutex.unlock(); } int main(int argc, char *argv[]) { + + // ToDo: discriminar niveles de log y log a fichero segúna argumentos + /* + if (args.contains("-log")) + { + qInstallMessageHandler(MessageHandler); + }*/ + QApplication app(argc, argv); + qInstallMessageHandler(MessageHandler); QStringList args = app.arguments(); // parse the command line if (args.size() > 1) @@ -98,10 +145,6 @@ int main(int argc, char *argv[]) qDebug() << "-h this help"; return 0; } - if (args.contains("-log")) - { - qInstallMessageHandler(MessageHandler); - } } libreMediaServerAudio libreMediaServerAudio(args); libreMediaServerAudio.show(); diff --git a/src/olathread.cpp b/src/olathread.cpp index b19bef2..366a644 100644 --- a/src/olathread.cpp +++ b/src/olathread.cpp @@ -2,6 +2,7 @@ olaThread::olaThread(QObject *parent) { + Q_UNUSED(parent); m_universe = new QList(); m_counter = 0; gettimeofday(&m_last_data, NULL); @@ -27,7 +28,7 @@ olaThread::olaThread(QObject *parent) ola::InitLogging(ola::OLA_LOG_INFO , ola::OLA_LOG_STDERR); m_client->SetDMXCallback(ola::NewCallback(this, &olaThread::NewDmx)); m_clientWrapper->GetSelectServer()->RegisterRepeatingTimeout(4000, ola::NewCallback(this, &olaThread::CheckDataLoss)); - qDebug() << "Init olaThread"; +// qDebug() << "Init olaThread"; } // --- DECONSTRUCTOR --- @@ -40,7 +41,7 @@ olaThread::~olaThread() { void olaThread::run() { - qDebug() << tr("olaThread| Running"); +// qDebug() << tr("olaThread| Running"); emit toTerminal("Reading DMX"); m_clientWrapper->GetSelectServer()->Run(); } @@ -72,7 +73,7 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, { m_counter++; gettimeofday(&m_last_data, NULL); - int universe = data.universe; + uint universe = data.universe; for (int i = 0; i < m_layersNumber; i++) { // loop for reading the channels by layer. if((m_settings.at(i).universe == universe) && ( m_settings.at(i).address > -1 )) { // Compare if the layer is from this universe @@ -99,8 +100,8 @@ bool olaThread::CheckDataLoss() { timersub(&now, &m_last_data, &diff); if (diff.tv_sec > 4 || (diff.tv_sec == 4 && diff.tv_usec > 4000000)) { // loss of data - qDebug()<< "olaThread| Can not read one or several universes"; - emit toTerminal("Can not read one universe"); +// qDebug()<< "olaThread| Can not read one or several universes"; + emit toTerminal("olaThread: Can not read one universe"); // return false; // Retorna false para deshabilitar el callback } } @@ -111,14 +112,13 @@ void olaThread::setLayersNumber(int layersNumber) { m_layersNumber = layersNumbe void olaThread::resendDmx() { - emit toTerminal("Resending DMX info"); +// qDebug() << "Resending DMX info"; for (int i = 0; i < m_layersNumber; i++) { // loop for reading the channels by layer. for (int j = 0; j < LAYER_CHANNELS; j++){ emit dmxOutput(i, j, m_dmx[i][j]); // Connected with dmx slot in olaInterface. } } } - //////////////////////////////////////////////////////////////////////////////////////////// From 69ffe7974202f67a506acc31f612996ac27e0c60 Mon Sep 17 00:00:00 2001 From: santi Date: Sat, 12 Jul 2014 21:53:07 +0200 Subject: [PATCH 3/3] Added a DMX watchdog. Changed the play methos, now play reset to 0 and stop not. --- src/audiodecoder.cpp | 11 +- src/audiolayerwidget.cpp | 198 +++++++++++++++++++++------------ src/audiolayerwidget.h | 22 +++- src/audiowidget.cpp | 10 +- src/audiowidget.h | 1 + src/defines.h | 30 ++--- src/libremediaserver-audio.cpp | 40 ++++--- src/libremediaserver-audio.h | 8 +- src/main.cpp | 53 ++++++--- src/medialibrary.cpp | 2 +- src/olathread.cpp | 38 +++++-- src/olathread.h | 15 +-- src/settings.cpp | 2 +- 13 files changed, 276 insertions(+), 154 deletions(-) diff --git a/src/audiodecoder.cpp b/src/audiodecoder.cpp index 41542ae..bff8f2c 100644 --- a/src/audiodecoder.cpp +++ b/src/audiodecoder.cpp @@ -106,16 +106,16 @@ void AudioDecoder::errorDecoding(QAudioDecoder::Error msg) case QAudioDecoder::NoError: break; case QAudioDecoder::ResourceError: - qWarning() << "A media resource couldn't be resolved: " << m_decoder->sourceFilename(); + qWarning("A media resource couldn't be resolved: %s", m_decoder->sourceFilename().toLatin1().constData()); break; case QAudioDecoder::FormatError: - qWarning() << "The format of a media resource isn't supported: " << m_decoder->sourceFilename();; + qWarning("The format of a media resource isn't supported: %s", m_decoder->sourceFilename().toLatin1().constData()); break; case QAudioDecoder::AccessDeniedError: - qWarning() << "There are not the appropriate permissions to play a media resource" << m_decoder->sourceFilename();; + qWarning("There are not the appropriate permissions to play a media resource %s", m_decoder->sourceFilename().toLatin1().constData()); break; case QAudioDecoder::ServiceMissingError: - qWarning() << "A valid playback service was not found, playback cannot proceed."; + qWarning("A valid playback service was not found, playback cannot proceed"); break; } } @@ -127,7 +127,6 @@ qint64 AudioDecoder::readData(char *data, qint64 len) qint64 total = 0; while (len - total > 0) { const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total); -// qWarning() << "Buffer Size: " << m_buffer.size() << "m_pos: " << m_pos; memcpy(data + total, m_buffer.constData() + m_pos, chunk); // Controla Final del track if ( (m_pos + chunk ) >= ( m_buffer.size() ) @@ -153,7 +152,7 @@ qint64 AudioDecoder::bytesAvailable() const void AudioDecoder::decoderFinished() { - qDebug() << "AudioDecoder: Decoding file finished; " << m_decoder->sourceFilename(); + qDebug("Decoding file finished %s ", m_decoder->sourceFilename().toLatin1().constData()); emit fileLoaded(m_decoder->sourceFilename()); } diff --git a/src/audiolayerwidget.cpp b/src/audiolayerwidget.cpp index eece14d..ed57bbc 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -9,16 +9,22 @@ 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(" ") + { this->setTitle(name); QVBoxLayout *layout = new QVBoxLayout; + m_deviceBox = new QComboBox(this); const QAudioDeviceInfo &defaultDeviceInfo = QAudioDeviceInfo::defaultOutputDevice(); m_deviceBox->addItem(defaultDeviceInfo.deviceName(), qVariantFromValue(defaultDeviceInfo)); @@ -26,6 +32,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): 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); @@ -34,6 +41,10 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): m_statusLabel = new QLabel; m_statusLabel->setText(STATUS_LABEL); m_statusValue = new QLabel; + m_receiveDMX = new QCheckBox("Receiving DMX", this); +// m_receiveDMX->setCheckable(false); + m_receiveDMX->setChecked(false); + status->addWidget(m_receiveDMX); status->addWidget(m_statusLabel); status->addWidget(m_statusValue); layout->addLayout(status); @@ -54,23 +65,43 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): fileLoaded->addWidget(m_fileValue); layout->addLayout(fileLoaded); - m_suspendResumeButton = new QPushButton(this); - m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); - connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); - layout->addWidget(m_suspendResumeButton); - 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(100); + 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); + + +// QHBoxLayout *totalTime = new QHBoxLayout; + 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(totalTime); + layout->addLayout(progressTime); + QHBoxLayout *progressSlider = new QHBoxLayout; m_progressLabel = new QLabel; m_progressLabel->setText(PROGRESS_LABEL); @@ -79,22 +110,10 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): progressSlider->addWidget(m_progressSlider); layout->addLayout(progressSlider); - QHBoxLayout *progressTime = new QHBoxLayout; - m_progressTimeLabel = new QLabel; - m_progressTimeLabel->setText(PROGRESS_TIME_LABEL); - m_progressTimeValue = new QLabel; - m_progressTimeValue->setText("0:00:00:000"); - progressTime->addWidget(m_progressTimeLabel); - progressTime->addWidget(m_progressTimeValue); - layout->addLayout(progressTime); - - QHBoxLayout *totalTime = new QHBoxLayout; - m_totalTimeLabel = new QLabel; - m_totalTimeLabel->setText(TOTAL_TIME_LABEL); - m_totalTimeValue = new QLabel; - totalTime->addWidget(m_totalTimeLabel); - totalTime->addWidget(m_totalTimeValue); - layout->addLayout(totalTime); + 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); @@ -107,7 +126,7 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); if (!info.isFormatSupported(m_format)) { - qWarning() << "Default format not supported - trying to use nearest"; + qWarning("Default format not supported - trying to use nearest"); m_format = info.nearestFormat(m_format); } m_decoder = new AudioDecoder(m_format, this); @@ -116,6 +135,10 @@ AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): 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(); } @@ -127,17 +150,17 @@ AudioLayerWidget::~AudioLayerWidget() void AudioLayerWidget::createAudioOutput() { m_audioOutput = new QAudioOutput(m_device, m_format, this); - m_audioOutput->setNotifyInterval(100); + Q_CHECK_PTR(m_audioOutput); + m_audioOutput->setNotifyInterval(NOTIFY_INTERVAL); connect(m_audioOutput, SIGNAL(notify()), SLOT(notified())); + reset(); + m_statusValue->setText(PAUSE_LABEL); connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State))); - m_output = m_audioOutput->start(); - toggleSuspendResume(); } void AudioLayerWidget::deviceChanged(int index) { m_pullTimer->stop(); - m_decoder->stop(); m_audioOutput->stop(); m_audioOutput->disconnect(this); m_device = m_deviceBox->itemData(index).value(); @@ -161,20 +184,31 @@ void AudioLayerWidget::setVol(qreal vol) void AudioLayerWidget::loadMedia(QString file) { - stop(); + if (m_currentMedia == file) + return; + reset(); if (QFile::exists(file)){ m_decoder->loadMedia(file); + 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() { - QTime real(0,0,0,0); - qint64 ms = m_audioOutput->processedUSecs() / 1000 ; - real = real.addMSecs(ms); - m_progressTimeValue->setText(real.toString("h:mm:ss:zzz")); - m_progressSlider->setValue(ms); + 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(); @@ -189,7 +223,7 @@ void AudioLayerWidget::pullTimerExpired() const qint64 len = m_decoder->read(m_buffer.data(), m_audioOutput->periodSize()); if ( len == -1) { stop(); - qDebug()<< this->title() << " End of file"; + qDebug("End of file %s", this->title().toLatin1().constData()); break; } if (len) { @@ -205,72 +239,92 @@ void AudioLayerWidget::pullTimerExpired() void AudioLayerWidget::toggleSuspendResume() { if (m_audioOutput->state() == QAudio::SuspendedState) { -// qDebug() << "status: Suspended, resume()"; - m_audioOutput->resume(); - m_pullTimer->start(20); - m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); - m_statusValue->setText(PLAY_LABEL); + resume(); } else if (m_audioOutput->state() == QAudio::ActiveState) { -// qDebug() << "status: Active, suspend()"; - m_audioOutput->suspend(); - m_pullTimer->stop(); - m_suspendResumeButton->setText(tr(RESUME_LABEL)); - m_statusValue->setText(PAUSE_LABEL); + pause(); } else if (m_audioOutput->state() == QAudio::StoppedState) { -// qDebug() << "status: Stopped, resume()"; play(); } else if (m_audioOutput->state() == QAudio::IdleState) { -// qDebug() << "status: IdleState"; - m_statusValue->setText(IDDLE_LABEL); + qWarning("%s: IdleState", this->title().toLatin1().constData()); + play(); } } void AudioLayerWidget::handleStateChanged(QAudio::State state) { - qDebug() << this->title() << " state = " << state; + if (state == QAudio::SuspendedState) { + m_statusValue->setText(PAUSE_LABEL); + } else if (state == QAudio::ActiveState) { + m_statusValue->setText(PLAY_LABEL); + } else if (m_audioOutput->state() == QAudio::StoppedState) { + m_statusValue->setText(STOP_LABEL); + } else if (state == QAudio::IdleState) { + m_statusValue->setText(IDDLE_LABEL); + } } void AudioLayerWidget::durationChanged(qint64 dur) { if (dur == -1) return; - QTime temp(0,0,0,0); - QTime real = temp.addMSecs(dur); - m_totalTimeValue->setText(real.toString("h:mm:ss:zzz")); + m_totalTimeValue->setTime(QTime::fromMSecsSinceStartOfDay(dur)); m_progressSlider->setMaximum(dur); - qDebug() << this->title() << " Duration Changed: " << dur; -} - -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::play() { -// m_decoder->setPos(0); - m_pullTimer->start(20); + setInitPosition(); + m_pullTimer->start(PULL_TIMER_INTERVAL); m_audioOutput->resume(); - m_statusValue->setText(PLAY_LABEL); + pullTimerExpired(); + m_progressCounter->start(); m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } void AudioLayerWidget::pause() { - toggleSuspendResume(); + m_audioOutput->suspend(); + m_pullTimer->stop(); + m_suspendResumeButton->setText(tr(RESUME_LABEL)); +} + +void AudioLayerWidget::resume() +{ + m_pullTimer->start(PULL_TIMER_INTERVAL); + m_audioOutput->resume(); + pullTimerExpired(); + m_progressCounter->start(); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); } void AudioLayerWidget::stop() { - m_audioOutput->suspend(); - m_pullTimer->stop(); -// m_audioOutput->reset(); - m_decoder->setPos(44); - m_statusValue->setText(STOP_LABEL); - m_suspendResumeButton->setText(tr(RESUME_LABEL)); + reset(); + setInitPosition(); +} + +void AudioLayerWidget::reset() +{ + m_pullTimer->stop(); + m_audioOutput->reset(); + m_output = m_audioOutput->start(); + Q_CHECK_PTR(m_output); + pause(); +} + +void AudioLayerWidget::setInitPosition() +{ + 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); } diff --git a/src/audiolayerwidget.h b/src/audiolayerwidget.h index d380b5b..14eed99 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -8,7 +8,10 @@ #include #include #include +#include +#include #include +#include #include "audiodecoder.h" #include "defines.h" @@ -27,7 +30,9 @@ public: void stop(); void pause(); void setVol(qreal vol); + void resume(); + void updateWatchDMX(bool b); public slots: void toggleSuspendResume(); @@ -47,10 +52,10 @@ private: QSlider *m_progressSlider; QLabel *m_progressTimeLabel; - QLabel *m_progressTimeValue; + QTimeEdit *m_progressTime; QLabel *m_totalTimeLabel; - QLabel *m_totalTimeValue; + QTimeEdit *m_totalTimeValue; QLabel *m_fileLabel; QLabel *m_fileValue; @@ -58,6 +63,9 @@ private: QLabel * m_folderLabel; QLabel * m_folderValue; + QCheckBox *m_receiveDMX; + QTimer *m_watchDMX; + QTimer *m_pullTimer; QAudioDeviceInfo m_device; QAudioOutput *m_audioOutput; @@ -66,8 +74,16 @@ private: QByteArray m_buffer; AudioDecoder *m_decoder; + QTime *m_progressCounter; + quint64 m_progressMs; + + QString m_currentMedia; + void createAudioOutput(); + void reset(); + void setInitPosition(); + private slots: void notified(); void pullTimerExpired(); @@ -75,7 +91,7 @@ private slots: void handleStateChanged(QAudio::State state); void deviceChanged(int index); void durationChanged(qint64 dur); - + void watchDMXExpired(); }; diff --git a/src/audiowidget.cpp b/src/audiowidget.cpp index 05004e2..51a4be9 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -35,11 +35,17 @@ void AudioWidget::playbackChanged(int layer, QAudio::State state) dynamic_cast(item->widget())->play(); break; case QAudio::StoppedState: - dynamic_cast(item->widget())->stop(); + dynamic_cast(item->widget())->pause(); break; case QAudio::SuspendedState: - dynamic_cast(item->widget())->toggleSuspendResume(); + dynamic_cast(item->widget())->resume(); case QAudio::IdleState: break; } } + +void AudioWidget::layerReceived(int layer) +{ + QLayoutItem * const item = layout->itemAt(layer); + dynamic_cast(item->widget())->updateWatchDMX(true); +} diff --git a/src/audiowidget.h b/src/audiowidget.h index 6ac58b5..6654fae 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -24,6 +24,7 @@ public slots: void mediaLoaded(int layer, QString media); void volChanged(int layer, qreal vol); void playbackChanged(int layer, QAudio::State state); + void layerReceived(int layer); }; #endif // AUDIOWIDGET_H diff --git a/src/defines.h b/src/defines.h index d46e749..648734a 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,7 +1,6 @@ #ifndef DEFINES_H #define DEFINES_H - #define VERSION "Libre Media Server Audio 0.1.0" #define COPYRIGHT "(C) 2014 Santi Norena libremediaserver@gmail.com" #define LICENSE "GPL 3 License. See LICENSE.txt and credits.txt for details" @@ -13,22 +12,27 @@ const int DurationSeconds = 1; const int ToneSampleRateHz = 600; const int DataSampleRateHz = 44100; -const int BufferSize = 32768; +const int BufferSize = 262144; -#define SUSPEND_LABEL "Suspend playback" -#define RESUME_LABEL "Resume playback" -#define PLAY_LABEL "Playing" -#define STOP_LABEL "Stopped" -#define PAUSE_LABEL "Pause" -#define IDDLE_LABEL "Iddle playback" -#define VOLUME_LABEL "Volume:" -#define PROGRESS_LABEL "Progress: " -#define PROGRESS_TIME_LABEL "Current Time: " +#define SUSPEND_LABEL "Pause playback" +#define RESUME_LABEL "Resume playback" + +#define PLAY_LABEL "Playing" +#define STOP_LABEL "Stopped" +#define PAUSE_LABEL "Pause" +#define IDDLE_LABEL "Iddle playback" + +#define VOLUME_LABEL "Volume" +#define PROGRESS_LABEL "Progress" +#define PROGRESS_TIME_LABEL "Current" #define REMAINING_TIME "Remaining Time: " -#define TOTAL_TIME_LABEL "Total Time: " +#define TOTAL_TIME_LABEL "Total" #define FILE_LABEL "File: " #define FOLDER_LABEL "Folder: " -#define STATUS_LABEL "Status: " +#define STATUS_LABEL " Status: " + +#define NOTIFY_INTERVAL 150 +#define PULL_TIMER_INTERVAL 10 // struct where save the DMX settings for each layer struct dmxSetting { diff --git a/src/libremediaserver-audio.cpp b/src/libremediaserver-audio.cpp index 9c6ba60..b71822a 100755 --- a/src/libremediaserver-audio.cpp +++ b/src/libremediaserver-audio.cpp @@ -33,14 +33,21 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) Q_UNUSED(args); ui.setupUi(this); - // Inicia el widget Terminal - textEdit = new QTextEdit; - textEdit->setReadOnly(true); - QDockWidget *bottomWidget = new QDockWidget(tr("Terminal"), this); - bottomWidget->setAllowedAreas(Qt::BottomDockWidgetArea); - bottomWidget->setWidget(textEdit); - addDockWidget(Qt::BottomDockWidgetArea, bottomWidget); + // Inicia la lectura de dmx a través de ola + ola = new olaThread(); + Q_CHECK_PTR(ola); + if (args.contains("-log")) { + // Inicia el widget Terminal + textEdit = new QTextEdit; + textEdit->setReadOnly(true); + QDockWidget *bottomWidget = new QDockWidget(tr("Terminal"), this); + bottomWidget->setAllowedAreas(Qt::BottomDockWidgetArea); + bottomWidget->setWidget(textEdit); + addDockWidget(Qt::BottomDockWidgetArea, bottomWidget); + connect(ola, SIGNAL(toTerminal(QString)), + textEdit, SLOT(append(QString)), Qt::QueuedConnection); + } this->setWindowTitle(VERSION); // qDebug() << QDate::currentDate().toString() << " "<< QTime::currentTime().toString(); @@ -52,13 +59,7 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) aw = new AudioWidget(this); setCentralWidget(aw); - // Inicia la lectura de dmx a través de ola - ola = new olaThread(); - Q_CHECK_PTR(ola); - - connect(ola, SIGNAL(toTerminal(QString)), - textEdit, SLOT(append(QString)), Qt::QueuedConnection); - + // Inicia la lectur de datos DMX ola->blockSignals(true); ola->start(QThread::TimeCriticalPriority ); @@ -88,13 +89,14 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) ola, SLOT( setLayersNumber(int))); connect(set, SIGNAL( DMXConf(dmxSetting ) ), ola, SLOT( setDMXConf(dmxSetting) ) ); - - connect(ola, SIGNAL( dmxOutput(int, int, int) ), - this, SLOT( dmxInput(int, int, int) ) ); + connect(ola, SIGNAL (layerReceived(int)), + aw, SLOT(layerReceived(int))); // Lee la configuración por defecto set->readDefaultFile(); ola->blockSignals(false); + connect(ola, SIGNAL( dmxOutput(int, int, int) ), + this, SLOT( dmxInput(int, int, int) ) ); ola->resendDmx(); } @@ -173,6 +175,8 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) { // This qDebug slows all the program. Uncomment only for debugging purpouse and comment again in normal use // qDebug() << tr("olaInterface|") << "newdmx layer" << layer << "channel" << channel << "value" << value; + if (layer > LAYER_CHANNELS) + return; QString mediaFile = NULL; int aux; qreal f; @@ -201,6 +205,8 @@ void libreMediaServerAudio::dmxInput(int layer, int channel, int value) f = (float)value / 255; break; case PLAYBACK: + if (value == 0) + break; aux = value / 25; switch (aux) { case 0 : diff --git a/src/libremediaserver-audio.h b/src/libremediaserver-audio.h index a05e21b..d59d7d1 100755 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -16,8 +16,8 @@ along with this program. If not, see . */ -#ifndef LIBREMEDIASERVER_H -#define LIBREMEDIASERVER_H +#ifndef LIBREMEDIASERVERAUDIO_H +#define LIBREMEDIASERVERAUDIO_H #include #include @@ -59,7 +59,7 @@ protected: private: -// void MessageHandler(QtMsgType type, const QMessageLogContext &logcontext, const QString &msg); + // void MessageHandler(QtMsgType type, const QMessageLogContext &logcontext, const QString &msg); AudioMasterWidget *amw; AudioWidget *aw; olaThread *ola; @@ -88,4 +88,4 @@ private slots: void ChangeMediaPath();// Change the path to medias }; -#endif // LIBREMEDIASERVER_H +#endif // LIBREMEDIASERVERAUDIO_H diff --git a/src/main.cpp b/src/main.cpp index d00acb0..89fa504 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -79,29 +79,54 @@ void MessageHandler(QtMsgType type, const QMessageLogContext &context, const QSt QString txt; switch (type) { case QtDebugMsg: - txt.append("Debug: "); + txt.append("Debug from File: "); txt.append(context.file); - txt.append(context.line); + txt.append(" Line: "); + txt.append(QString::number(context.line)); + txt.append(" Function: "); txt.append(context.function); + txt.append(" Message: "); txt.append(msg); // libreMediaServerAudio::textEdit->append(msg);, context.file, context.line, context.function); libreMediaServerAudio::textEdit->append(txt); break; case QtWarningMsg: -// txt.append("Warning: "); -// txt.append(msg); - libreMediaServerAudio::textEdit->append(msg); + txt.append("Warning from File: "); + txt.append(context.file); + txt.append(" Line: "); + txt.append(QString::number(context.line)); + txt.append(" Function: "); + txt.append(context.function); + txt.append(" Message: "); + txt.append(msg); +// libreMediaServerAudio::textEdit->append(msg);, context.file, context.line, context.function); + libreMediaServerAudio::textEdit->append(txt); + abort(); break; case QtCriticalMsg: -// txt.append("Critical: "); -// txt.append(msg); - libreMediaServerAudio::textEdit->append(msg); +// txt.append("Critical from File: "); + txt.append(context.file); + txt.append(" Line: "); + txt.append(QString::number(context.line)); + txt.append(" Function: "); + txt.append(context.function); + txt.append(" Message "); + txt.append(msg); +// libreMediaServerAudio::textEdit->append(msg);, context.file, context.line, context.function); + libreMediaServerAudio::textEdit->append(txt); + abort(); break; case QtFatalMsg: -// txt.append("Fatal: "); -// txt.append(msg); - libreMediaServerAudio::textEdit->append(msg); -// ts << txt << endl; +// txt.append("Fatal from File: "); + txt.append(context.file); + txt.append(" Line: "); + txt.append(QString::number(context.line)); + txt.append(" Function: "); + txt.append(context.function); + txt.append(" Message: "); + txt.append(msg); +// libreMediaServerAudio::textEdit->append(msg);, context.file, context.line, context.function); + libreMediaServerAudio::textEdit->append(txt);// ts << txt << endl; abort(); } // outFile.write(txt.toLatin1().constData(), txt.size()); @@ -120,9 +145,9 @@ int main(int argc, char *argv[]) { qInstallMessageHandler(MessageHandler); }*/ - - QApplication app(argc, argv); qInstallMessageHandler(MessageHandler); + QApplication app(argc, argv); + QStringList args = app.arguments(); // parse the command line if (args.size() > 1) diff --git a/src/medialibrary.cpp b/src/medialibrary.cpp index 9299b51..d884d38 100644 --- a/src/medialibrary.cpp +++ b/src/medialibrary.cpp @@ -27,7 +27,7 @@ MediaLibrary::MediaLibrary(QObject *parent) : void MediaLibrary::initMediaLibrary() { QDir dir; if (!dir.cd(m_pathmedia)) { - qWarning() << "olaInterface::initMediaLibrary| Can not cd to the path: " << m_pathmedia.toLatin1().constData(); + qWarning("Can not cd to the path: %s", m_pathmedia.toLatin1().constData()); return; } m_media = new QList; diff --git a/src/olathread.cpp b/src/olathread.cpp index 366a644..52388f4 100644 --- a/src/olathread.cpp +++ b/src/olathread.cpp @@ -75,18 +75,19 @@ void olaThread::NewDmx(const ola::client::DMXMetadata &data, gettimeofday(&m_last_data, NULL); uint universe = data.universe; for (int i = 0; i < m_layersNumber; i++) { // loop for reading the channels by layer. - if((m_settings.at(i).universe == universe) - && ( m_settings.at(i).address > -1 )) { // Compare if the layer is from this universe - // AND if the DMX address is 0 or greater, process this layer. + if(m_settings.at(i).universe == universe) { // Compare if the layer is from this universe + if ( m_settings.at(i).address > -1 ) { // AND if the DMX address is 0 or greater, process this layer. + emit layerReceived(i); for (int j = 0; j < LAYER_CHANNELS; j++){ - int value = buffer.Get((m_settings.at(i).address) + j); // Get the value for this channel. - if (m_dmx[i][j] != value) { // Compare the new value with the old value. - emit dmxOutput(i,j,value); // Connected with dmx slot in olaInterface. - m_dmx[i][j] = value; - } + int value = buffer.Get((m_settings.at(i).address) + j); // Get the value for this channel. + if (m_dmx[i][j] != value) { // Compare the new value with the old value. + emit dmxOutput(i,j,value); + m_dmx[i][j] = value; } + } } - } + } + } } /** @@ -108,7 +109,13 @@ bool olaThread::CheckDataLoss() { return true; } -void olaThread::setLayersNumber(int layersNumber) { m_layersNumber = layersNumber; } +void olaThread::setLayersNumber(int layersNumber) +{ + if (layersNumber <= LAYERS_NUMBER) + m_layersNumber = layersNumber; + else + m_layersNumber = LAYERS_NUMBER; +} void olaThread::resendDmx() { @@ -119,6 +126,17 @@ void olaThread::resendDmx() } } } + +void olaThread::setDMXConf(dmxSetting set) +{ + if (set.layer >= m_layersNumber) { return; } + m_settings.replace(set.layer, set); + // ToDo: registro del nuevo universo si no está registrado ya + if (!m_universe->contains(set.universe)) { + registerUniverse(set.universe); + m_universe->append(set.universe); + } +} //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/olathread.h b/src/olathread.h index c91998f..3e0d589 100644 --- a/src/olathread.h +++ b/src/olathread.h @@ -67,10 +67,10 @@ private: // typedef SingleUseCallback1 ola::client::SetCallback inline void RegisterComplete(const ola::client::Result &error) { if (error.Success()) { - qDebug() << "Register Universe success"; +// qDebug("Register Universe success"); emit toTerminal("Register Universe success"); } else { - qWarning() << "olaThread|" << "Register command failed" << QString::fromStdString(error.Error()); +// qWarning("Register command failed: %s", error.Error().c_str()); emit toTerminal("olaThread| Register command failed " + QString::fromStdString(error.Error())); } } @@ -84,15 +84,7 @@ public slots: void stop(); // Close the connection with olad. void setLayersNumber(int layersNumber); - inline void setDMXConf(dmxSetting set) { - if (set.layer >= m_layersNumber) { return; } - m_settings.replace(set.layer, set); - // ToDo: registro del nuevo universo si no está registrado ya - if (!m_universe->contains(set.universe)) { - registerUniverse(set.universe); - m_universe->append(set.universe); - } - } + void setDMXConf(dmxSetting set); protected slots: @@ -101,6 +93,7 @@ signals: void finished(); // Signal for closing. Not used now. void dmxOutput(int layer, int channel, int value); // Signal when a channel has changed void toTerminal(QString message); + void layerReceived(int i); }; #endif // OLATHREAD_H diff --git a/src/settings.cpp b/src/settings.cpp index 85a2f49..ff2bc10 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -23,7 +23,7 @@ void Settings::readFromFile(QString file) { QXmlStreamReader* xmlReader = new QXmlStreamReader(xmlFile); int counter = 0; //Parse the XML until we reach end of it - while(!xmlReader->atEnd() && !xmlReader->hasError()) { + while(!xmlReader->atEnd() && !xmlReader->hasError() && counter < LAYERS_NUMBER) { // Read next element QXmlStreamReader::TokenType token = xmlReader->readNext(); //If token is just StartDocument - go to next