lms-video/externals/artnetin/artnetin.c
2013-01-09 14:12:27 +01:00

301 lines
9.5 KiB
C
Executable file

/*
* artnetin is an external for Pure Data that reads an artnet universe
* and outputs it in an outlet like a list of 512 int.
*
* artnetin needs libartnet installed in the sytem to work
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* Santiago Noreña
* puremediaserver@gmail.com
* Versión 0.0.4
*
* Based on previous works by:
* Dirk Jagdmann (doj@cubic.org) dmxmonitor
* Simon Newton (nomis52@westnet.com.au) libartnet and modified dmxmonitor to use ArtNet
* Patrick Sébastien http://www.workinprogress.ca/ pure data external pthread template
*
*/
/*
* ToDo:
* - Cambiar funciones Unix por funciones PD (getbytes, freebytes,...)
* - cambiar variables a syntaxis pure data (x_subnet)
* - método max channels distinto de 512
* - meter thread_start en función principal
*
*/
#include "m_pd.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "pthread.h"
#include "artnet.h"
#include <malloc.h>
// ==============================================================================
// Our External's Memory structure
// ------------------------------------------------------------------------------
t_class *artnetin_class;
typedef struct _artnetin
{
t_object x_obj;
artnet_node node; // el nodo artnet
int CHANNELS; // la longitud del array dmx
t_outlet *outlet1; // outlet pointer
int subnet; // variables subnet y universo; solo utiles para mostrar informacion del nodo en bang
int universe;
pthread_attr_t artnetin_thread_attr;
pthread_t x_threadid; // Control de thread
char *ip; // variable para definir el interface donde se creará el nodo.
} t_artnetin;
unsigned char *dmx; // El array DMX
// ==============================================================================
// dmx handler -
// ------------------------------------------------------------------------------
static int dmx_handler(artnet_node node, int prt , void *d) {
int z;
int len;
t_artnetin *x = (t_artnetin*) d;
uint8_t *g = dmx; // Puntero al array dmx
uint8_t *data; // Puntero
data = artnet_read_dmx(node, prt, &len) ; // Nos devuelve el array dmx en data?
memcpy(g, data, len) ; // Copia el array dmx devuelto a g
t_atom dmxa[x->CHANNELS]; // Crea un array dmx en formato float pure dat
uint8_t *b = dmx; // Otro puntero a dmx; no podemos usar g?
for(z=0; z < x->CHANNELS; z++){ // Copiamos el array dmx al array dmxa
SETFLOAT(dmxa+z, *b);
b++;
}
outlet_list(x->outlet1 ,gensym("list"), x->CHANNELS, dmxa); // Saca el array dmxa mediante una lista
return (0);
}
//--------------------------------------------------------------------------
// - bang - Devuelve información del estado del nodo
//--------------------------------------------------------------------------
void artnetin_bang(t_artnetin *x) {
if ((int)x->x_threadid == 0) {
post ("Artnetin: Node not created");
}
else
{
post ("Artnetin: Node created on thread %d", x->x_threadid);
post ("Artnetin: listening on Subnet %d Universe %d and IP address %s", x->subnet, x->universe, x->ip);
}
return;
}
// =============================================================================
// Worker Thread
// =============================================================================
static void *thread_read(void *w)
{
t_artnetin *x = (t_artnetin*) w;
int n, max, z;
int i = 0;
int artnet_sd;
pthread_testcancel();
while(1) {
artnet_sd = artnet_get_sd(x->node) ;
fd_set rd_fds;
struct timeval tv;
FD_ZERO(&rd_fds);
FD_SET(0, &rd_fds);
FD_SET(artnet_sd, &rd_fds) ;
max = artnet_sd ;
tv.tv_sec = 1;
tv.tv_usec = 0;
n = select(max+1, &rd_fds, NULL, NULL, &tv);
if(n>0) {
if (FD_ISSET(artnet_sd , &rd_fds)){
i = artnet_read(x->node,0);
}
if (i == 0) {
t_atom dmxa[x->CHANNELS];
uint8_t *b = dmx;
for(z=0; z<x->CHANNELS; z++){
SETFLOAT(dmxa+z, *b);
b++;
}
outlet_list(x->outlet1 ,gensym("list"), x->CHANNELS, dmxa);
}
else {
error("Artnetin: Bucle Read Error");
}
}
}
return 0;
}
// =============================================================================
// Start Thread
// =============================================================================
static void thread_start(t_artnetin *x)
{
// create the worker thread
if(pthread_attr_init(&x->artnetin_thread_attr) < 0)
{
sys_lock();
error("Artnetin: could not launch receive thread");
sys_unlock();
return;
}
if(pthread_attr_setdetachstate(&x->artnetin_thread_attr, PTHREAD_CREATE_DETACHED) < 0)
{
sys_lock();
error("Artnetin: could not launch receive thread");
sys_unlock();
return;
}
if(pthread_create(&x->x_threadid, &x->artnetin_thread_attr, thread_read, x) < 0)
{
sys_lock();
error("Artnetin: could not launch receive thread");
sys_unlock();
return;
}
else
{
sys_lock();
post("Artnetin: thread %d launched", (int)x->x_threadid );
sys_unlock();
}
}
//--------------------------------------------------------------------------
// - Message: ip - define el interface donde escuchará el nodo mediante la ip
//--------------------------------------------------------------------------
void artnetin_ip(t_artnetin *x, t_symbol *ip_addres)
{
x->ip = getbytes(15);
strcpy(x->ip, ip_addres->s_name);
post("Artnetin: IP %s", x->ip);
}
//--------------------------------------------------------------------------
// - Message: open - Empieza el nodo artnet y lanza el thread que escuchará en ese nodo
//--------------------------------------------------------------------------
void artnetin_open(t_artnetin *x, t_floatarg f1, t_floatarg f2)
{
if ((int)x->x_threadid == 0) {
x->subnet = f1;
x->universe = f2;
post("Artnetin: opening node Artnet Subnet %d Universe %d IP %s", x->subnet, x->universe, x->ip);
// char * ip_addr = NULL;
x->node = artnet_new(x->ip, 0 );
if(x->node == NULL) {
error("Artnetin: Unable to connect") ;
}
artnet_set_dmx_handler(x->node, dmx_handler, x);
artnet_set_subnet_addr(x->node, x->subnet);
artnet_set_port_type(x->node, 0, ARTNET_ENABLE_OUTPUT, ARTNET_PORT_DMX) ;
artnet_set_port_addr(x->node, 0, ARTNET_OUTPUT_PORT, x->universe);
artnet_set_long_name(x->node, "Artnetin Pure Data 0.0.4");
artnet_set_short_name(x->node, "Artnetin-PD");
artnet_start(x->node);
int i = artnet_read(x->node,0);
if (i == 0) {
post("Artnetin: Init OK");
thread_start(x);
}
else error ("Artnetin: Init Error");
}
else
{
error ("Artnetin: Node created. Close it before try reopen");
}
}
//--------------------------------------------------------------------------
// - Message: close
//--------------------------------------------------------------------------
void artnetin_close(t_artnetin *x)
{
if ((int)x->x_threadid == 0) {
error("Artnetin: node not created. Doing nothing");
}
else {
post("Artnetin: closing node");
artnet_stop(x->node) ;
artnet_destroy(x->node) ;
while(pthread_cancel(x->x_threadid) < 0);
x->x_threadid = 0;
}
}
//--------------------------------------------------------------------------
// - Initialization
//--------------------------------------------------------------------------
void *artnetin_new(void)
{
post("Artnetin: an ArtNet interface : v0.0.4");
post(" written by Santiago Noreña (puremediaserver@gmail.com)");
t_artnetin *x = (t_artnetin *)pd_new(artnetin_class); // local variable (pointer to a t_artnetin data structure)
x->outlet1 = outlet_new(&x->x_obj, &s_list); // Saca todos los canales mediante una lista
x->CHANNELS = 512; // Define el número de canales del array dmx
dmx = malloc(512) ; // localiza memoria para el array dmx; Cambiar por getbytes
if(!dmx) {
error("Artnetin: malloc failed") ;
}
memset(dmx, 0x00, x->CHANNELS) ; // Inicializa el array dmx a 0
x->ip = NULL; // Inicializa la dirección ip a NULL = interface con ip más baja
return (void *)x;
}
//--------------------------------------------------------------------------
// - Object destruction
//--------------------------------------------------------------------------
void artnetin_free(t_artnetin *x)
{
artnetin_close(x);
freebytes(x->ip, 15);
}
//--------------------------------------------------------------------------
// - Object creation and setup
//--------------------------------------------------------------------------
void artnetin_setup(void)
{
artnetin_class = class_new ( gensym("artnetin"),(t_newmethod)artnetin_new, 0, sizeof(t_artnetin), CLASS_DEFAULT, A_GIMME, 0);
// Add message handlers
class_addmethod(artnetin_class, (t_method)artnetin_open, gensym("open"), A_DEFFLOAT, A_DEFFLOAT, 0);
class_addmethod(artnetin_class, (t_method)artnetin_close, gensym("close"), 0);
class_addmethod(artnetin_class, (t_method)artnetin_ip, gensym("ip"), A_SYMBOL, 0);
class_addbang(artnetin_class, artnetin_bang);
}