diff --git a/AUTHORS b/AUTHORS index 00d38f5..4a7be32 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,5 @@ Gauthier Legrand Romuald Conty +Michiel Brink diff --git a/Core/artdmx.cpp b/Core/artdmx.cpp new file mode 100644 index 0000000..294c50d --- /dev/null +++ b/Core/artdmx.cpp @@ -0,0 +1,152 @@ +/* dmxmain.c forkt from https://github.com/ohm2013loc/art/blob/master/dmxmain.c */ + +#include +#include +#include +#include +#include +#include +#include + +#define OpOutput 0x5000 + +#define VERSION_HI 0 +#define VERSION_LO 14 + +#define ARTNET_PORT "6454" + + +struct pkt_ArtOutput { + char ID[8]; + unsigned short OpCode; + unsigned char ProtVerHi; + unsigned char ProtVerLo; + unsigned char Sequence; + unsigned char Physical; + unsigned char SubUni; + unsigned char Net; + unsigned char LengthHi; + unsigned char Length; + unsigned char Data[512]; +} __attribute__((packed)); + +#define DMXPKTLEN(channels) (channels+18) + +int art_socket; + +struct addrinfo *dmx_dest; + +void setup_socket(char *universe_address) +{ + struct addrinfo hints, *res; + struct sockaddr_in myaddr; + struct timeval timeout; + int rv; + + timeout.tv_sec = 3; + timeout.tv_usec = 0; + + int yes = 1; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + getaddrinfo("255.255.255.255", ARTNET_PORT, &hints, &res); + + art_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (art_socket == -1) { + perror("socket"); + exit(1); + } + +#if 0 + if (bind(art_socket, res->ai_addr, res->ai_addrlen) == -1) { + perror("bind"); + exit(1); + } +#endif + + if (setsockopt(art_socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) + == -1) { + perror("setsockopt"); + exit(1); + } + + if (setsockopt(art_socket,SOL_SOCKET,SO_BROADCAST,&yes,sizeof(int)) + == -1) { + perror("setsockopt"); + exit(1); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + + if ((rv = getaddrinfo(universe_address, ARTNET_PORT, &hints, &dmx_dest)) + != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + exit(1); + } + +} + +void send_dmx(struct addrinfo *dest, char *data, int length) +{ + struct pkt_ArtOutput pkt; + int i; + int numbytes; + static unsigned char dmx_sequence; + + strcpy(pkt.ID, "Art-Net"); + pkt.OpCode = OpOutput; + pkt.ProtVerHi = VERSION_HI; + pkt.ProtVerLo = VERSION_LO; + pkt.Sequence = dmx_sequence++; + if (dmx_sequence == 0) + dmx_sequence++; + pkt.Physical = 0; + pkt.SubUni = 0; + pkt.Net = 0; + pkt.LengthHi = (length >> 8); + pkt.Length = length & 0xff; + memcpy(pkt.Data, data, length); + + if ((numbytes = sendto(art_socket, &pkt, DMXPKTLEN(length), 0, + dest->ai_addr, dest->ai_addrlen)) == -1) { + perror("sendto"); + exit(1); + } +} + +struct timeval delay_time; + +void delay_setup(void) +{ + gettimeofday(&delay_time, NULL); +} + +void frame_delay(unsigned int microseconds) +{ + struct timeval interval; + struct timeval now; + struct timespec int_ts; + + interval.tv_sec = microseconds / 1000000; + interval.tv_usec = microseconds % 1000000; + + timeradd(&delay_time, &interval, &delay_time); + + gettimeofday(&now, NULL); + + do { + timersub(&delay_time, &now, &interval); + + int_ts.tv_sec = interval.tv_sec; + int_ts.tv_nsec = interval.tv_usec * 1000; + + nanosleep(&int_ts, NULL); + + gettimeofday(&now, NULL); + } while (timercmp(&delay_time, &now, >)); +} diff --git a/Core/artdmx.h b/Core/artdmx.h new file mode 100644 index 0000000..c2efbd5 --- /dev/null +++ b/Core/artdmx.h @@ -0,0 +1,21 @@ +/* +dmxmain.h forkt from https://github.com/ohm2013loc/art/blob/master/dmxmain.c +*/ + +#ifndef dmxmain_H +#define dmxmain_H + +#include +#include +#include + +extern struct addrinfo *dmx_dest; + +void setup_socket(char *universe_address); +void send_dmx(struct addrinfo *dest, char *data, int length); +void delay_setup(void); +void frame_delay(unsigned int microseconds); + + +#endif // dmxmain_H + diff --git a/Core/ledmatrix.cpp b/Core/ledmatrix.cpp index 96a3d14..d2bf6bd 100644 --- a/Core/ledmatrix.cpp +++ b/Core/ledmatrix.cpp @@ -1,6 +1,7 @@ /* * Copyright 2012, 2013 Gauthier Legrand * Copyright 2012, 2013 Romuald Conty + * Copyright 2015, 2017 Michiel Brink * * This file is part of Minotor. * @@ -29,7 +30,8 @@ LedMatrix::LedMatrix(const QSize size, const QSize panelSize, const QSize matrix _panelSize(panelSize), _matrixSize(matrixSize), _port(NULL), - _connected(false) + _connected_tty(false), + _connected_art(false) { _port = new QextSerialPort(); @@ -39,7 +41,7 @@ LedMatrix::LedMatrix(const QSize size, const QSize panelSize, const QSize matrix LedMatrix::~LedMatrix() { - if (_connected) _port->close(); + if (_connected_tty) _port->close(); delete _port; } @@ -49,12 +51,21 @@ bool LedMatrix::openPortByName(const QString& portName) _port->setBaudRate(BAUD1000000); if (_port->open(QIODevice::WriteOnly)){ qDebug() << "Led matrix connected to:" << this->portName(); - _connected = true; + _connected_tty = true; emit(connected()); } else { qDebug() << "Led matrix failed to connect to:" << portName; } - return _connected; + return _connected_tty; +} + +bool LedMatrix::openArtByIp(const QString& artIp) +{ + qDebug() << "ip address:" << artIp; + setup_socket((char*)artIp.toStdString().c_str()); + delay_setup(); + _connected_art = true; + return _connected_art; } void LedMatrix::closePort() @@ -62,14 +73,14 @@ void LedMatrix::closePort() if (_port) { _port->close(); qDebug() << "Led matrix disconnected."; - _connected = false; + _connected_tty = false; emit(connected(false)); } } bool LedMatrix::isConnected() { - return _connected; + return _connected_tty; } QString LedMatrix::portName() const @@ -120,20 +131,21 @@ void LedMatrix::computeLookUpTable() for (unsigned int y=0;ywrite(_framebuffer.constData(),_framebuffer.size()); char endFrame = 0x01; _port->write(&endFrame,1); } + + if(_connected_art) + { + dmx_universe = (char*)_framebuffer.constData(); + send_dmx(dmx_dest, dmx_universe, 512); + frame_delay(20000); + } emit(updated()); } } diff --git a/Core/ledmatrix.h b/Core/ledmatrix.h index 755e43e..5d0dacb 100644 --- a/Core/ledmatrix.h +++ b/Core/ledmatrix.h @@ -1,6 +1,7 @@ /* * Copyright 2012, 2013 Gauthier Legrand * Copyright 2012, 2013 Romuald Conty + * Copyright 2015, 2017 Michiel Brink * * This file is part of Minotor. * @@ -28,6 +29,8 @@ #include +#include "artdmx.h" + #include "qextserialport.h" class LedMatrix : public QObject @@ -38,6 +41,7 @@ class LedMatrix : public QObject ~LedMatrix(); bool openPortByName(const QString &portName); + bool openArtByIp(const QString &artIp); void closePort(); QString portName() const; @@ -61,7 +65,13 @@ class LedMatrix : public QObject // Connection QextSerialPort *_port; - bool _connected; + bool _connected_tty; + bool _connected_art; + + //artnet + char* dmx_universe; + std::vector dmx_universe_temp; + char* _ipAdress; // Returns true if LedMatrix is fully configured (ie. does have all requiered sizes sets) bool isConfigured() const; diff --git a/Core/minotor.cpp b/Core/minotor.cpp index 26f9c77..0ab5269 100644 --- a/Core/minotor.cpp +++ b/Core/minotor.cpp @@ -1,6 +1,7 @@ /* * Copyright 2012, 2013 Gauthier Legrand * Copyright 2012, 2013 Romuald Conty + * Copyright 2015, 2017 Michiel Brink * * This file is part of Minotor. * diff --git a/Core/minotor.h b/Core/minotor.h index 692f8d9..5df8dd7 100644 --- a/Core/minotor.h +++ b/Core/minotor.h @@ -1,6 +1,7 @@ /* * Copyright 2012, 2013 Gauthier Legrand * Copyright 2012, 2013 Romuald Conty + * Copyright 2015, 2017 Michiel Brink * * This file is part of Minotor. * diff --git a/Minotor.pro b/Minotor.pro index e863ffd..898c122 100644 --- a/Minotor.pro +++ b/Minotor.pro @@ -30,6 +30,7 @@ SOURCES += \ Animation/minatext.cpp \ Animation/minavibration.cpp \ Animation/minawaveform.cpp \ + Core/artdmx.cpp \ Core/Midi/midi.cpp \ Core/Midi/midicontrol.cpp \ Core/Midi/midicontrollablelist.cpp \ @@ -105,6 +106,7 @@ HEADERS += \ Animation/minatext.h \ Animation/minavibration.h \ Animation/minawaveform.h \ + Core/artdmx.h \ Core/Midi/midi.h \ Core/Midi/midicontrol.h \ Core/Midi/midicontrollablelist.h \ @@ -211,11 +213,11 @@ clang.commands = scan-build make clean all clang.depends = qmake_clang cppcheck.commands = \ - cppcheck --quiet --enable=all \ - --force --inconclusive \ - -i libraries \ - -I libraries/qextserialport/src \ - ./ + cppcheck --quiet --enable=all \ + --force --inconclusive \ + -i libraries \ + -I libraries/qextserialport/src \ + ./ QMAKE_EXTRA_TARGETS += clang qmake_clang cppcheck dmg diff --git a/README.md b/README.md index 2638140..faa2376 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,13 @@ sudo apt install minotor ### With system-wide rtmdi ~~~ -sudo apt install qtbase5-dev librtmidi-dev libqt5extserialport-dev +sudo apt install qt5-defaults librtmidi-dev libqt5extserialport-dev ~~~ ### With embedded rtmidi, without JACK support ~~~ -sudo apt install qtbase5-dev libasound2-dev libqt5extserialport-dev +sudo apt install qt5-defaults libasound2-dev libqt5extserialport-dev ~~~ ## Compilation diff --git a/Ui/configdialog.cpp b/Ui/configdialog.cpp index f05309d..e178efd 100644 --- a/Ui/configdialog.cpp +++ b/Ui/configdialog.cpp @@ -1,6 +1,8 @@ /* * Copyright 2012, 2013 Gauthier Legrand * Copyright 2012, 2013 Romuald Conty + * Copyright 2015, 2017 Michiel Brink + * * This file is part of Minotor. * @@ -72,6 +74,7 @@ ConfigDialog::ConfigDialog(QWidget *parent) : updateMidiTab(); updateMidiMappingTab(); updateSerialTab(); + updateArtnetTab(); disconnect(Minotor::minotor()->midi(), SIGNAL(controlChanged(int,quint8,quint8,quint8)), this, SLOT(midiControlChanged(int,quint8,quint8,quint8))); ui->tableMidiMapping->setColumnWidth(0,250); } @@ -180,6 +183,9 @@ void ConfigDialog::on_tabWidget_currentChanged(int index) case 3: // Serial updateSerialTab(); break; + case 4: // Artnet + updateArtnetTab(); + break; } } @@ -454,6 +460,13 @@ void ConfigDialog::updateSerialTab() ui->cbSerialPort->setCurrentIndex(currentItem); } +void ConfigDialog::updateArtnetTab() +{ + LedMatrix *matrix = Minotor::minotor()->ledMatrix(); + // Clear combobox + ui->leArtnet->clear(); +} + void ConfigDialog::updateGeneralTab() { QSize size = Minotor::minotor()->rendererSize(); @@ -494,6 +507,22 @@ void ConfigDialog::on_pbSerialConnect_clicked(bool checked) } } +void ConfigDialog::on_pbArtnet_clicked(bool checked) +{ + if (checked) + { + Minotor::minotor()->ledMatrix()->openArtByIp(ui->leArtnet->text()); + if(!Minotor::minotor()->ledMatrix()->isConnected()) + { + //qDebug() << Q_FUNC_INFO + // << "Unable to connect"; + ui->pbArtnet->setChecked(false); + } + } else { + //Minotor::minotor()->ledMatrix()->closePort(); + } +} + void ConfigDialog::configDialogFinished(int result) { qDebug() << Q_FUNC_INFO diff --git a/Ui/configdialog.h b/Ui/configdialog.h index 2e8da4b..a6f151b 100644 --- a/Ui/configdialog.h +++ b/Ui/configdialog.h @@ -1,6 +1,7 @@ /* * Copyright 2012, 2013 Gauthier Legrand * Copyright 2012, 2013 Romuald Conty + * Copyright 2015, 2017 Michiel Brink * * This file is part of Minotor. * @@ -55,6 +56,8 @@ private slots: void on_pbSerialConnect_clicked(bool checked); + void on_pbArtnet_clicked(bool checked); + void configDialogFinished(int result); void on_pbSaveAs_clicked(); @@ -77,6 +80,7 @@ private slots: void updateMidiTab(); void updateMidiMappingTab(); void updateSerialTab(); + void updateArtnetTab(); QTimer *_midiAutoRefreshTimer; void addMidiMappingEntry(QFileInfo file, QComboBox *cb); diff --git a/Ui/configdialog.ui b/Ui/configdialog.ui index eb5b837..6ec3008 100644 --- a/Ui/configdialog.ui +++ b/Ui/configdialog.ui @@ -140,6 +140,24 @@ + + + + + 9 + + + 9 + + + 9 + + + 9 + + + + @@ -827,6 +845,269 @@ + + + Artnet + + + + + 10 + 65 + 938 + 311 + + + + + + + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + IP address + + + + + + + + + + + + + + + + + Connect + + + true + + + + + + + + + + + + + + 100 + 0 + + + + Matrix type + + + + + + + + Single panel + + + + + Multi panel + + + + + + + + + + + + + + Pixels X + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Pixels Y + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + 0 + + + 0 + + + + + + + + Panel pixels X + + + + + + + + + + + + + + + + Panel count X + + + + + + + + + + + + + + + + Panel pixels Y + + + + + + + + + + + + + + + + Panel count Y + + + + + + + + + + + + + + + + + + 10 + 10 + 938 + 49 + + + + + 9 + + + + + + 100 + 0 + + + + Protocol + + + + + + + + Artnet + + + + + + +