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 {