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/src/audiodecoder.cpp b/src/audiodecoder.cpp new file mode 100644 index 0000000..bff8f2c --- /dev/null +++ b/src/audiodecoder.cpp @@ -0,0 +1,167 @@ +#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: %s", m_decoder->sourceFilename().toLatin1().constData()); + break; + case QAudioDecoder::FormatError: + 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 %s", m_decoder->sourceFilename().toLatin1().constData()); + 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); + 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("Decoding file finished %s ", m_decoder->sourceFilename().toLatin1().constData()); + 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..ed57bbc 100644 --- a/src/audiolayerwidget.cpp +++ b/src/audiolayerwidget.cpp @@ -1,22 +1,145 @@ #include "audiolayerwidget.h" +#include +#include +#include +#include + AudioLayerWidget::AudioLayerWidget(QWidget *parent, QString name): QGroupBox(parent) -{ - folder = new QLabel(this); - file = new QLabel(this); - status = new QLabel(this); - vol = new QSlider(this); - vol->setMaximum(99); - mute = new QCheckBox(this); + , 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(" ") - QVBoxLayout *vbox = new QVBoxLayout; - vbox->addWidget(folder); - vbox->addWidget(file); - vbox->addWidget(status); - vbox->addWidget(vol); - vbox->addWidget(mute); - this->setLayout(vbox); +{ + this->setTitle(name); + + QVBoxLayout *layout = new QVBoxLayout; + + m_deviceBox = new QComboBox(this); + const QAudioDeviceInfo &defaultDeviceInfo = QAudioDeviceInfo::defaultOutputDevice(); + m_deviceBox->addItem(defaultDeviceInfo.deviceName(), qVariantFromValue(defaultDeviceInfo)); + foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) { + if (deviceInfo != defaultDeviceInfo) + m_deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo)); + } + m_deviceBox->setMaximumWidth(250); + connect(m_deviceBox,SIGNAL(activated(int)), + this, SLOT(deviceChanged(int))); + layout->addWidget(m_deviceBox); + + QHBoxLayout *status = new QHBoxLayout; + m_statusLabel = new QLabel; + m_statusLabel->setText(STATUS_LABEL); + m_statusValue = new QLabel; + m_receiveDMX = new QCheckBox("Receiving DMX", this); +// m_receiveDMX->setCheckable(false); + m_receiveDMX->setChecked(false); + status->addWidget(m_receiveDMX); + status->addWidget(m_statusLabel); + status->addWidget(m_statusValue); + layout->addLayout(status); + + QHBoxLayout *folderLoaded = new QHBoxLayout; + m_folderLabel = new QLabel; + m_folderLabel->setText(FOLDER_LABEL); + m_folderValue = new QLabel; + 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); + + QHBoxLayout *volumeBox = new QHBoxLayout; + m_volumeLabel = new QLabel; + m_volumeLabel->setText(tr(VOLUME_LABEL)); + m_volumeSlider = new QSlider(Qt::Horizontal); + m_volumeSlider->setMinimum(0); + m_volumeSlider->setMaximum(80); + m_volumeSlider->setSingleStep(1); + connect(m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int))); + volumeBox->addWidget(m_volumeLabel); + volumeBox->addWidget(m_volumeSlider); + layout->addLayout(volumeBox); + + QHBoxLayout *progressTime = new QHBoxLayout; + m_progressTimeLabel = new QLabel; + m_progressTimeLabel->setText(PROGRESS_TIME_LABEL); + m_progressTime = new QTimeEdit; + m_progressTime->setDisplayFormat("h:mm:ss:zzz"); + m_progressTime->setReadOnly(true); + m_progressTime->setButtonSymbols(QAbstractSpinBox::NoButtons); + m_progressTime->setMaximumWidth(90); + progressTime->addWidget(m_progressTimeLabel); + progressTime->addWidget(m_progressTime); + + +// 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); + m_progressSlider = new QSlider(Qt::Horizontal); + progressSlider->addWidget(m_progressLabel); + progressSlider->addWidget(m_progressSlider); + layout->addLayout(progressSlider); + + m_suspendResumeButton = new QPushButton(this); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); + connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume())); + layout->addWidget(m_suspendResumeButton); + + this->setLayout(layout); + + m_format.setSampleRate(DataSampleRateHz); + m_format.setChannelCount(2); + m_format.setSampleSize(16); + m_format.setCodec("audio/pcm"); + m_format.setByteOrder(QAudioFormat::LittleEndian); + m_format.setSampleType(QAudioFormat::SignedInt); + + QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); + if (!info.isFormatSupported(m_format)) { + qWarning("Default format not supported - trying to use nearest"); + m_format = info.nearestFormat(m_format); + } + m_decoder = new AudioDecoder(m_format, this); + m_decoder->start(); + connect(m_decoder, SIGNAL(totalTimeChanged(qint64)), + this, SLOT(durationChanged(qint64))); + connect(m_pullTimer, SIGNAL(timeout()), + this, SLOT(pullTimerExpired())); + + connect(m_watchDMX, SIGNAL(timeout()), + this, SLOT(watchDMXExpired())); + m_watchDMX->start(1000); + createAudioOutput(); } AudioLayerWidget::~AudioLayerWidget() @@ -24,3 +147,184 @@ AudioLayerWidget::~AudioLayerWidget() } +void AudioLayerWidget::createAudioOutput() +{ + m_audioOutput = new QAudioOutput(m_device, m_format, this); + 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))); +} + +void AudioLayerWidget::deviceChanged(int index) +{ + m_pullTimer->stop(); + m_audioOutput->stop(); + m_audioOutput->disconnect(this); + m_device = m_deviceBox->itemData(index).value(); + createAudioOutput(); +} + +void AudioLayerWidget::volumeChanged(int value) +{ + if (m_audioOutput) + m_audioOutput->setVolume(qreal(value/100.0f)); +} + +void AudioLayerWidget::setVol(qreal vol) +{ + m_audioOutput->setVolume(vol); + m_volumeSlider->blockSignals(true); + int volume = vol * 100; + m_volumeSlider->setValue(volume); + m_volumeSlider->blockSignals(false); +} + +void AudioLayerWidget::loadMedia(QString file) +{ + if (m_currentMedia == file) + return; + 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() +{ + m_progressMs += m_progressCounter->restart(); + m_progressTime->setTime(QTime::fromMSecsSinceStartOfDay(m_progressMs)); + m_progressSlider->setValue(m_progressMs); +/* qDebug() << "bytesFree = " << m_audioOutput->bytesFree() + << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs() + << ", " << "processedUSecs = " << m_audioOutput->processedUSecs(); +*/ +} + +void AudioLayerWidget::pullTimerExpired() +{ + if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) { + int chunks = m_audioOutput->bytesFree()/m_audioOutput->periodSize(); + while (chunks) { + const qint64 len = m_decoder->read(m_buffer.data(), m_audioOutput->periodSize()); + if ( len == -1) { + stop(); + qDebug("End of file %s", this->title().toLatin1().constData()); + break; + } + if (len) { + m_output->write(m_buffer.data(), len); + } + if (len != m_audioOutput->periodSize()) + break; + --chunks; + } + } +} + +void AudioLayerWidget::toggleSuspendResume() +{ + if (m_audioOutput->state() == QAudio::SuspendedState) { + resume(); + } else if (m_audioOutput->state() == QAudio::ActiveState) { + pause(); + } else if (m_audioOutput->state() == QAudio::StoppedState) { + play(); + } else if (m_audioOutput->state() == QAudio::IdleState) { + qWarning("%s: IdleState", this->title().toLatin1().constData()); + play(); + } +} + +void AudioLayerWidget::handleStateChanged(QAudio::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; + m_totalTimeValue->setTime(QTime::fromMSecsSinceStartOfDay(dur)); + m_progressSlider->setMaximum(dur); +} + +void AudioLayerWidget::play() +{ + setInitPosition(); + m_pullTimer->start(PULL_TIMER_INTERVAL); + m_audioOutput->resume(); + pullTimerExpired(); + m_progressCounter->start(); + m_suspendResumeButton->setText(tr(SUSPEND_LABEL)); +} + +void AudioLayerWidget::pause() +{ + 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() +{ + 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 3384dcf..14eed99 100644 --- a/src/audiolayerwidget.h +++ b/src/audiolayerwidget.h @@ -1,13 +1,20 @@ #ifndef AUDIOLAYERWIDGET_H #define AUDIOLAYERWIDGET_H -#include +#include +#include +#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 +22,78 @@ 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); + void resume(); - inline void setFolder(QString folder) const - { - this->folder->setText(folder); - } + void updateWatchDMX(bool b); +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; + QTimeEdit *m_progressTime; + + QLabel *m_totalTimeLabel; + QTimeEdit *m_totalTimeValue; + + QLabel *m_fileLabel; + QLabel *m_fileValue; + + QLabel * m_folderLabel; + QLabel * m_folderValue; + + QCheckBox *m_receiveDMX; + QTimer *m_watchDMX; + + QTimer *m_pullTimer; + QAudioDeviceInfo m_device; + QAudioOutput *m_audioOutput; + QIODevice *m_output; // not owned + QAudioFormat m_format; + 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(); + void fileLoaded(QString file); + void handleStateChanged(QAudio::State state); + void deviceChanged(int index); + void durationChanged(qint64 dur); + void watchDMXExpired(); }; + + #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..51a4be9 100644 --- a/src/audiowidget.cpp +++ b/src/audiowidget.cpp @@ -12,20 +12,40 @@ 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())->pause(); + break; + case QAudio::SuspendedState: + 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 6b39808..6654fae 100644 --- a/src/audiowidget.h +++ b/src/audiowidget.h @@ -10,25 +10,21 @@ 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); + void layerReceived(int layer); }; #endif // AUDIOWIDGET_H diff --git a/src/defines.h b/src/defines.h index 9e221b9..648734a 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,8 +1,61 @@ #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" + #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 = 262144; + +#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" +#define FILE_LABEL "File: " +#define FOLDER_LABEL "Folder: " +#define STATUS_LABEL " Status: " + +#define NOTIFY_INTERVAL 150 +#define PULL_TIMER_INTERVAL 10 + +// 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..b71822a 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,39 +30,45 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) : QMainWindow(parent) { - qDebug() << "********************************************************************************"; - qDebug() << QDate::currentDate() << QTime::currentTime(); - qDebug() << VERSION; - qDebug() << COPYRIGHT; - qDebug() << LICENSE; - - // Inicia el User Interface + Q_UNUSED(args); ui.setupUi(this); - // Inicia el widget Terminal - textEdit = new QTextEdit(this); - textEdit->append(QString::fromAscii(VERSION)); - textEdit->append(QString::fromAscii(LICENSE)); - textEdit->append(QString::fromAscii(COPYRIGHT)); - - 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); - connect(ola, SIGNAL(toTerminal(QString)), - this, SLOT(toTerminal(QString))); - ola->start(QThread::TimeCriticalPriority ); - ola->blockSignals(true); + + 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(); + qDebug() << VERSION; + qDebug() << COPYRIGHT; + qDebug() << LICENSE; // Inicia el widget central de audio - aw = new AudioWidget(this); setCentralWidget(aw); + // Inicia la lectur de datos DMX + 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); @@ -66,22 +77,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"); - } - // Conectamos los menus connect(ui.actionOpen_conf, SIGNAL(triggered()), this, SLOT(openFile())); connect(ui.actionSave_conf, SIGNAL(triggered()), this, SLOT(saveFile())); @@ -90,30 +85,19 @@ 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) ) ); + 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(); } /////////////////////////////////////////////////////////////////// @@ -123,13 +107,11 @@ libreMediaServerAudio::libreMediaServerAudio(QStringList args, QWidget *parent) libreMediaServerAudio::~libreMediaServerAudio() { // save_finish(); - delete MediaLibrary::getInstance(); - ola->stop(); - AudioMotor::getInstance()->close(); -// qDebug() << "PD Audio restarts: " << m_startaudio; - qDebug() << QDate::currentDate() << QTime::currentTime(); - qDebug() << "********************************************************************************"; - return; +// delete MediaLibrary::getInstance(); +// ola->stop(); +// qDebug() << QDate::currentDate().toString() << " " << QTime::currentTime().toString(); +// qDebug() << "********************************************************************************"; +// return; } /////////////////////////////////////////////////////////////////// @@ -171,7 +153,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); } @@ -193,49 +175,51 @@ 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; - 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: + if (value == 0) + break; 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..d59d7d1 100755 --- a/src/libremediaserver-audio.h +++ b/src/libremediaserver-audio.h @@ -16,15 +16,11 @@ along with this program. If not, see . */ -#ifndef LIBREMEDIASERVER_H -#define LIBREMEDIASERVER_H +#ifndef LIBREMEDIASERVERAUDIO_H +#define LIBREMEDIASERVERAUDIO_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 { @@ -61,35 +51,26 @@ public: Ui::LibreMediaServerAudio ui; + static QTextEdit *textEdit; // Terminal de feedback + protected: QString m_pathmedia; // Path to Medias - QProcess *m_ola; // OLA daemon process - - bool m_gui; private: - QTextEdit *textEdit; // Terminal de feedback + // void MessageHandler(QtMsgType type, const QMessageLogContext &logcontext, const QString &msg); 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: - - inline void toTerminal (QString message) - { - textEdit->append(message); - } +// inline void toTerminal(QString msg) { textEdit->append(msg); } private slots: @@ -98,16 +79,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 +#endif // LIBREMEDIASERVERAUDIO_H diff --git a/src/libremediaserver-audio.pro b/src/libremediaserver-audio.pro index a455f01..037f010 100755 --- a/src/libremediaserver-audio.pro +++ b/src/libremediaserver-audio.pro @@ -1,36 +1,39 @@ 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 #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 @@ -41,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 2e76bc9..89fa504 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,38 +19,25 @@ */ #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 char *msg) +void MessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { - QString txt; - switch (type) { - case QtDebugMsg: - txt = QString("Debug: %1").arg(msg); - break; - case QtWarningMsg: - txt = QString("Warning: %1").arg(msg); - break; - case QtCriticalMsg: - txt = QString("Critical: %1").arg(msg); - break; - case QtFatalMsg: - txt = QString("Fatal: %1").arg(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")) - { + if (!dir.exists("log")) { + if (!dir.mkdir("log")) { qDebug()<<"MessageHandler: Can not create log folder"; return; } @@ -62,57 +49,127 @@ 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; } - 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 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); + break; + case QtWarningMsg: + 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 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 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()); +// 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); + }*/ + qInstallMessageHandler(MessageHandler); 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("Can not cd to the path: %s", 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..52388f4 100644 --- a/src/olathread.cpp +++ b/src/olathread.cpp @@ -1,9 +1,8 @@ #include "olathread.h" - olaThread::olaThread(QObject *parent) -// : QObject(parent) { + Q_UNUSED(parent); m_universe = new QList(); m_counter = 0; gettimeofday(&m_last_data, NULL); @@ -21,11 +20,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); @@ -34,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 --- @@ -45,13 +39,9 @@ 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); - }*/ - qDebug() << tr("olaThread| Running"); +void olaThread::run() +{ +// qDebug() << tr("olaThread| Running"); emit toTerminal("Reading DMX"); m_clientWrapper->GetSelectServer()->Run(); } @@ -77,27 +67,27 @@ 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) { 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 - // 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; } + } } - } + } + } } /** @@ -111,19 +101,25 @@ 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 } } 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() { - 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. @@ -131,6 +127,16 @@ 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 ae44238..3e0d589 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,15 +64,13 @@ 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"; +// 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())); } } @@ -100,19 +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: @@ -121,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 2bfcbb8..ff2bc10 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); @@ -25,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 @@ -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 {