Qt5 upgrade and new audio engine based in QtMultimedia.

New GUI.
This commit is contained in:
santi 2014-07-10 01:51:29 +02:00
parent 6e268323d2
commit cccd987bdd
23 changed files with 724 additions and 983 deletions

46
.gitignore vendored Normal file
View file

@ -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

View file

@ -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;

View file

@ -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;

168
src/audiodecoder.cpp Normal file
View file

@ -0,0 +1,168 @@
#include "audiodecoder.h"
#include <QFile>
#include <qmath.h>
#include <qendian.h>
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<unsigned char *>(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<format.channelCount(); ++i) {
qint16 value = static_cast<qint16>(x * 32767);
if (format.byteOrder() == QAudioFormat::LittleEndian)
qToLittleEndian<qint16>(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;
}

45
src/audiodecoder.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef AUDIODECODER_H
#define AUDIODECODER_H
#include <QIODevice>
#include <QAudioFormat>
#include <QAudioDecoder>
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

View file

@ -1,22 +1,120 @@
#include "audiolayerwidget.h"
#include <QDebug>
#include <QVBoxLayout>
#include <QFile>
#include <QTime>
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<QAudioDeviceInfo>();
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));
}

View file

@ -1,13 +1,17 @@
#ifndef AUDIOLAYERWIDGET_H
#define AUDIOLAYERWIDGET_H
#include <QtGui>
#include <QAudioOutput>
#include <QByteArray>
#include <QComboBox>
#include <QLabel>
#include <QPushButton>
#include <QSlider>
#include <QTimer>
#include <QGroupBox>
//#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);
}
inline void setVol(float vol) const
{
this->vol->setValue(vol);
}
public slots:
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

View file

@ -3,6 +3,10 @@
#include <QLabel>
#include <QtGui>
#include <QGroupBox>
#include <QSlider>
#include <QCheckBox>
#include <QVBoxLayout>
//#include "ui_audiomasterwidget.h"

View file

@ -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");
}

View file

@ -1,143 +0,0 @@
#ifndef AUDIOMOTOR_H
#define AUDIOMOTOR_H
#include <QObject>
#include <QtDebug>
#include <QtNetwork>
#include <QTcpServer>
#include <QLocalSocket>
#include <QTcpSocket>
#include <QChar>
#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

View file

@ -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<AudioLayerWidget *>(item->widget())->setFolder(folder);
dynamic_cast<AudioLayerWidget *>(item->widget())->setFile(file);
QLayoutItem * const item = layout->itemAt(layer);
dynamic_cast<AudioLayerWidget *>(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<AudioLayerWidget *>(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<AudioLayerWidget *>(item->widget())->play();
break;
case QAudio::StoppedState:
dynamic_cast<AudioLayerWidget *>(item->widget())->stop();
break;
case QAudio::SuspendedState:
dynamic_cast<AudioLayerWidget *>(item->widget())->toggleSuspendResume();
case QAudio::IdleState:
break;
}
}

View file

@ -10,25 +10,20 @@
class AudioWidget : public QWidget
{
Q_OBJECT
public:
AudioWidget(QWidget *parent);
private:
// QList<AudioLayerWidget*> *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

View file

@ -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<MediaFile> m_MediaInformation; // Pointer to the Medias Information List of this Library
};
#endif // DEFINES_H

View file

@ -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;
}
}

View file

@ -20,11 +20,7 @@
#define LIBREMEDIASERVER_H
#include <QMainWindow>
//#include <QApplication>
#include <QObject>
#include <QDesktopWidget>
#include <QtGui>
#include <QDockWidget>
#include <QFile>
#include <QFileInfo>
#include <QFileDialog>
@ -35,20 +31,14 @@
#include <QTextEdit>
#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,17 +54,12 @@ 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();
@ -82,8 +67,6 @@ private:
void open(QFile *file);
void save(QFile *file);
// void MessageHandler(QtMsgType type, const char *msg);
public slots:
inline void toTerminal (QString message)
@ -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

View file

@ -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

View file

@ -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<args.size();i++)
{
qDebug() << "Option not known: " << args.at(i);
}
qDebug() <<"Write ./libremediaserver-audio -h for help in command line arguments";
return 0;
qInstallMessageHandler(MessageHandler);
}
}
libreMediaServerAudio libreMediaServerAudio(args);

View file

@ -1,5 +1,6 @@
#include "medialibrary.h"
#include <QDebug>
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<MediaFolder>;
@ -88,5 +88,3 @@ QString MediaLibrary::requestNewFile(int folder, int file){
}
return newfile;
}

View file

@ -4,21 +4,7 @@
#include <QObject>
#include <QDir>
// 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<MediaFile> 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<MediaFile> getMediaInformation(QDir dir); // Get all the information of each media file in a dir
signals:

View file

@ -1,8 +1,6 @@
#include "olathread.h"
olaThread::olaThread(QObject *parent)
// : QObject(parent)
{
m_universe = new QList<int>();
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<void, const DMXMetadata&, const DmxBuffer&> ola::client::RepeatableDMXCallback
void olaThread::NewDmx(const ola::client::DMXMetadata &data,
const ola::DmxBuffer &buffer)

View file

@ -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<int> *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<dmxSetting> 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<void, const Result&> 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);

View file

@ -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++;
}

View file

@ -7,10 +7,8 @@
#include <QMessageBox>
#include "olathread.h"
#include "audiomotor.h"
#include "medialibrary.h"
#define DEFAULT_FILE "lms-audio.xlm"
#include "defines.h"
class Settings : public QObject
{