multithreaded app
This commit is contained in:
139
RpiLedBars/src/tasks/artnet/rpi_artnet.c
Normal file
139
RpiLedBars/src/tasks/artnet/rpi_artnet.c
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "rpi_artnet.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "rpi_artnet_utils.h"
|
||||
|
||||
int udpSocket = -1;
|
||||
char buffer[1024];
|
||||
bool isUdpListenerRunning = false;
|
||||
pthread_t udpListener;
|
||||
uint8_t artDmxBufferArray[16][512];
|
||||
|
||||
static void *artnet_udp_handler(void *arg);
|
||||
|
||||
void artnet_send_poll_reply(struct sockaddr_in srcAddr);
|
||||
|
||||
void artnet_init() {}
|
||||
|
||||
void start_artnet_bg_worker() {
|
||||
isUdpListenerRunning = true;
|
||||
|
||||
if (pthread_create(&udpListener, NULL, artnet_udp_handler, NULL) < 0) {
|
||||
perror("pthread_create");
|
||||
}
|
||||
}
|
||||
|
||||
void stop_artnet_bg_worker() {
|
||||
isUdpListenerRunning = false;
|
||||
|
||||
if (pthread_join(udpListener, NULL) != 0) {
|
||||
perror("pthread_join");
|
||||
}
|
||||
}
|
||||
|
||||
int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData) {
|
||||
if (univerve > 8) {
|
||||
fprintf(stderr, "Universe %d out of bounds %d\n", univerve, 16);
|
||||
*dmxData = NULL;
|
||||
return -1;
|
||||
}
|
||||
*dmxData = artDmxBufferArray[univerve];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *artnet_udp_handler(void *arg) {
|
||||
struct sockaddr_in serverAddr;
|
||||
struct pollfd fds[1];
|
||||
int timeoutMs;
|
||||
int ret;
|
||||
int flags;
|
||||
|
||||
/* Create UDP socket */
|
||||
udpSocket = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (udpSocket < 0) {
|
||||
perror("Opening socket failed");
|
||||
}
|
||||
|
||||
/* Set non-blocking socket */
|
||||
flags = fcntl(udpSocket, F_GETFL, 0);
|
||||
fcntl(udpSocket, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
/* pollfd structure and timeout */
|
||||
memset(fds, 0, sizeof(fds));
|
||||
fds[0].fd = udpSocket;
|
||||
fds[0].events = POLLIN;
|
||||
timeoutMs = 10;
|
||||
|
||||
/* Configure settings in address struct */
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(ARTNET_PORT);
|
||||
serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
|
||||
memset(serverAddr.sin_zero, '\0', sizeof(serverAddr.sin_zero));
|
||||
|
||||
/* Bind socket with address struct */
|
||||
bind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
|
||||
|
||||
struct sockaddr_in srcAddr;
|
||||
socklen_t srcLen = sizeof(srcAddr);
|
||||
|
||||
while (isUdpListenerRunning) {
|
||||
|
||||
if ((ret = poll(fds, 1, timeoutMs)) == 1) {
|
||||
ssize_t bufferLen =
|
||||
recvfrom(udpSocket, buffer, ARTNET_MAX_BUFFER, 0, (struct sockaddr *)&srcAddr, &srcLen);
|
||||
|
||||
if (bufferLen <= ARTNET_MAX_BUFFER && bufferLen > sizeof(artnetHeader_t)) {
|
||||
artnetHeader_t *artnetHeader = (artnetHeader_t *)buffer;
|
||||
|
||||
if (memcmp(artnetHeader->id, ARTNET_ID, sizeof(ARTNET_ID)) == 0) {
|
||||
switch (artnetHeader->opCode) {
|
||||
case OpDmx:
|
||||
if (bufferLen >= 20) {
|
||||
artDmx_t *artDmx = (artDmx_t *)buffer;
|
||||
uint16_t dmxLength = (artDmx->lengthHi << 8) | artDmx->lengthLo;
|
||||
uint8_t *artDmxBuffer = artDmxBufferArray[artDmx->subUni & 0x00ff];
|
||||
if (dmxLength <= 512) {
|
||||
// store for later use
|
||||
memcpy(artDmxBuffer, artDmx->data, dmxLength);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OpPoll:
|
||||
artnet_send_poll_reply(srcAddr);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ret < 0) {
|
||||
fprintf(stderr, "error polling %d: %s\n", udpSocket, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
close(udpSocket);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void artnet_send_poll_reply(struct sockaddr_in srcAddr) {
|
||||
/* Configure settings in address struct */
|
||||
srcAddr.sin_family = AF_INET;
|
||||
srcAddr.sin_port = htons(ARTNET_PORT);
|
||||
memset(srcAddr.sin_zero, '\0', sizeof(srcAddr.sin_zero));
|
||||
|
||||
sendto(udpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0,
|
||||
(struct sockaddr *)&srcAddr, sizeof(srcAddr));
|
||||
}
|
14
RpiLedBars/src/tasks/artnet/rpi_artnet.h
Normal file
14
RpiLedBars/src/tasks/artnet/rpi_artnet.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#if !defined(__RPI_ARTNET_H__)
|
||||
#define __RPI_ARTNET_H__
|
||||
|
||||
#include "rpi_artnet_packets.h"
|
||||
|
||||
void artnet_init();
|
||||
|
||||
void start_artnet_bg_worker();
|
||||
|
||||
void stop_artnet_bg_worker();
|
||||
|
||||
int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData);
|
||||
|
||||
#endif // __RPI_ARTNET_H__
|
128
RpiLedBars/src/tasks/artnet/rpi_artnet_op_codes.h
Normal file
128
RpiLedBars/src/tasks/artnet/rpi_artnet_op_codes.h
Normal file
@@ -0,0 +1,128 @@
|
||||
#if !defined(__RPI_ARTNET_OP_CODES_H__)
|
||||
#define __RPI_ARTNET_OP_CODES_H__
|
||||
|
||||
/* List of hex values and discriptions of Opcodes */
|
||||
|
||||
/* This is an ArtPoll packet, no other data is contained in this UDP packet */
|
||||
#define OpPoll 0x2000
|
||||
|
||||
/* This is an ArtPollReply Packet. It contains device status information. */
|
||||
#define OpPollReply 0x2100
|
||||
|
||||
/* Diagnostics and data logging packet. */
|
||||
#define OpDiagData 0x2300
|
||||
|
||||
/* Used to send text based parameter commands. */
|
||||
#define OpCommand 0x2400
|
||||
|
||||
/* This is an ArtDmx data packet. It contains zero start code DMX512 information for a single
|
||||
* Universe. */
|
||||
#define OpOutput 0x5000
|
||||
|
||||
/* This is an ArtDmx data packet. It contains zero start code DMX512 information for a single
|
||||
* Universe. */
|
||||
#define OpDmx 0x5000
|
||||
|
||||
/* This is an ArtNzs data packet. It contains non-zero start code (except RDM) DMX512 information
|
||||
* for a single Universe. */
|
||||
#define OpNzs 0x5100
|
||||
|
||||
/* This is an ArtAddress packet. It contains remote programming information for a Node. */
|
||||
#define OpAddress 0x6000
|
||||
|
||||
/* This is an ArtInput packet. It contains enable – disable data for DMX inputs. */
|
||||
#define OpInput 0x7000
|
||||
|
||||
/* This is an ArtTodRequest packet. It is used to request a Table of Devices (ToD) for RDM
|
||||
* discovery. */
|
||||
#define OpTodRequest 0x8000
|
||||
|
||||
/* This is an ArtTodData packet. It is used to send a Table of Devices (ToD) for RDM discovery. */
|
||||
#define OpTodData 0x8100
|
||||
|
||||
/* This is an ArtTodControl packet. It is used to send RDM discovery control messages. */
|
||||
#define OpTodControl 0x8200
|
||||
|
||||
/* This is an ArtRdm packet. It is used to send all non discovery RDM messages. */
|
||||
#define OpRdm 0x8300
|
||||
|
||||
/* This is an ArtRdmSub packet. It is used to send compressed, RDM Sub-Device data. */
|
||||
#define OpRdmSub 0x8400
|
||||
|
||||
/* This is an ArtVideoSetup packet. It contains video screen setup information for nodes that
|
||||
* implement the extended video features. */
|
||||
#define OpVideoSetup 0xa010
|
||||
|
||||
/* This is an ArtVideoPalette packet. It contains colour palette setup information for nodes that
|
||||
* implement the extended video features. */
|
||||
#define OpVideoPalette 0xa020
|
||||
|
||||
/* This is an ArtVideoData packet. It contains display data for nodes that implement the extended
|
||||
* video features. */
|
||||
#define OpVideoData 0xa040
|
||||
|
||||
/* This is an ArtMacMaster packet. It is used to program the Node’s MAC address, Oem device type and
|
||||
* ESTA manufacturer code. This is for factory initialisation of a Node. It is not to be used by
|
||||
* applications. */
|
||||
#define OpMacMaster 0xf000
|
||||
|
||||
/* This is an ArtMacSlave packet. It is returned by the node to acknowledge receipt of an
|
||||
* ArtMacMaster packet. */
|
||||
#define OpMacSlave 0xf100
|
||||
|
||||
/* This is an ArtFirmwareMaster packet. It is used to upload new firmware or firmware extensions to
|
||||
* the Node. */
|
||||
#define OpFirmwareMaster 0xf200
|
||||
|
||||
/* This is an ArtFirmwareReply packet. It is returned by the node to acknowledge receipt of an
|
||||
* ArtFirmwareMaster packet or ArtFileTnMaster packet. */
|
||||
#define OpFirmwareReply 0xf300
|
||||
|
||||
/* Uploads user file to node. */
|
||||
#define OpFileTnMaster 0xf400
|
||||
|
||||
/* Downloads user file from node. */
|
||||
#define OpFileFnMaster 0xf500
|
||||
|
||||
/* Node acknowledge for downloads. */
|
||||
#define OpFileFnReply 0xf600
|
||||
|
||||
/* This is an ArtIpProg packet. It is used to reprogramme the IP, Mask and Port address of the Node.
|
||||
*/
|
||||
#define OpIpProg 0xf800
|
||||
|
||||
/* This is an ArtIpProgReply packet. It is returned by the node to acknowledge receipt of an
|
||||
* ArtIpProg packet. */
|
||||
#define OpIpProgReply 0xf900
|
||||
|
||||
/* This is an ArtMedia packet. It is Unicast by a Media Server and acted upon by a Controller. */
|
||||
#define OpMedia 0x9000
|
||||
|
||||
/* This is an ArtMediaPatch packet. It is Unicast by a Controller and acted upon by a Media Server.
|
||||
*/
|
||||
#define OpMediaPatch 0x9100
|
||||
|
||||
/* This is an ArtMediaControl packet. It is Unicast by a Controller and acted upon by a Media
|
||||
* Server. */
|
||||
#define OpMediaControl 0x9200
|
||||
|
||||
/* This is an ArtMediaControlReply packet. It is Unicast by a Media Server and acted upon by a
|
||||
* Controller. */
|
||||
#define OpMediaContrlReply 0x9300
|
||||
|
||||
/* This is an ArtTimeCode packet. It is used to transport time code over the network. */
|
||||
#define OpTimeCode 0x9700
|
||||
|
||||
/* Used to synchronise real time date and clock */
|
||||
#define OpTimeSync 0x9800
|
||||
|
||||
/* Used to send trigger macros */
|
||||
#define OpTrigger 0x9900
|
||||
|
||||
/* Requests a node's file list */
|
||||
#define OpDirectory 0x9a00
|
||||
|
||||
/* Replies to OpDirectory with file list */
|
||||
#define OpDirectoryReply 0x9b00
|
||||
|
||||
#endif // __RPI_ARTNET_OP_CODES_H__
|
178
RpiLedBars/src/tasks/artnet/rpi_artnet_packets.h
Normal file
178
RpiLedBars/src/tasks/artnet/rpi_artnet_packets.h
Normal file
@@ -0,0 +1,178 @@
|
||||
#if !defined(__RPI_ARTNET_PACKETS_H__)
|
||||
#define __RPI_ARTNET_PACKETS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "rpi_artnet_op_codes.h"
|
||||
#include "rpi_artnet_utils.h"
|
||||
|
||||
typedef struct {
|
||||
char id[8];
|
||||
uint16_t opCode;
|
||||
} __attribute__((__packed__)) artnetHeader_t;
|
||||
|
||||
typedef struct {
|
||||
artnetHeader_t artnetHeader;
|
||||
uint16_t protVer;
|
||||
uint8_t sequence;
|
||||
uint8_t physical;
|
||||
uint8_t subUni;
|
||||
uint8_t net;
|
||||
uint8_t lengthHi;
|
||||
uint8_t lengthLo;
|
||||
uint8_t data[512];
|
||||
} __attribute__((__packed__)) artDmx_t;
|
||||
|
||||
typedef struct {
|
||||
char ID[8];
|
||||
uint16_t OpCode;
|
||||
uint8_t IPAddr[4];
|
||||
uint16_t Port;
|
||||
uint16_t VersionInfo; // The node's current FIRMWARE VERS
|
||||
// Bits 14-8 of the 15 bit universe number are encoded into the bottom 7 bits of this field.
|
||||
uint8_t NetSwitch;
|
||||
// This is used in combination with SubSwitch and Swin[] or Swout[] to produce the full universe
|
||||
// address. Bits 7-4 of the 15 bit universe number are encoded into the bottom 4 bits of this
|
||||
// field.
|
||||
uint8_t SubSwitch;
|
||||
uint16_t Oem; // Manufacturer code, bit 15 set if
|
||||
// extended features avail
|
||||
uint8_t UbeaVersion; // Firmware version of UBEA
|
||||
uint8_t Status;
|
||||
// bit 0 = 0 UBEA not present
|
||||
// bit 0 = 1 UBEA present
|
||||
// bit 1 = 0 Not capable of RDM (Uni-directional DMX)
|
||||
// bit 1 = 1 Capable of RDM (Bi-directional DMX)
|
||||
// bit 2 = 0 Booted from flash (normal boot)
|
||||
// bit 2 = 1 Booted from ROM (possible error condition)
|
||||
// bit 3 = Not used
|
||||
// bit 54 = 00 Universe programming authority unknown
|
||||
// bit 54 = 01 Universe programming authority set by front panel controls
|
||||
// bit 54 = 10 Universe programming authority set by network
|
||||
// bit 76 = 00 Indicators Normal
|
||||
// bit 76 = 01 Indicators Locate
|
||||
// bit 76 = 10 Indicators Mute
|
||||
uint8_t EstaMan[2]; // ESTA manufacturer id, lo byte
|
||||
char ShortName[18]; // short name defaults to IP
|
||||
char LongName[64];
|
||||
// Text feedback of Node status or errors also used for debug info
|
||||
char NodeReport[64];
|
||||
|
||||
uint8_t NumPortsHi; // 0
|
||||
uint8_t NumPortsLo; // 4 If num i/p ports is dif to output ports, return biggest
|
||||
|
||||
uint8_t PortTypes[4];
|
||||
// bit 7 is output
|
||||
// bit 6 is input
|
||||
// bits 0-5 are protocol number (0= DMX, 1=MIDI)
|
||||
// for DMX-Hub ={0xc0,0xc0,0xc0,0xc0};
|
||||
|
||||
uint8_t GoodInput[4];
|
||||
// bit 7 is data received
|
||||
// bit 6 is data includes test packets
|
||||
// bit 5 is data includes SIP's
|
||||
// bit 4 is data includes text
|
||||
// bit 3 set is input is disabled
|
||||
// bit 2 is receive errors
|
||||
// bit 1-0 not used, transmitted as zero.
|
||||
// Don't test for zero!
|
||||
|
||||
uint8_t GoodOutput[4];
|
||||
// bit 7 is data is transmitting
|
||||
// bit 6 is data includes test packets
|
||||
// bit 5 is data includes SIP's
|
||||
// bit 4 is data includes text
|
||||
// bit 3 output is merging data.
|
||||
// bit 2 set if DMX output short detected on power up
|
||||
// bit 1 set if DMX output merge mode is LTP
|
||||
// bit 0 not used, transmitted as zero.
|
||||
|
||||
uint8_t SwIn[4];
|
||||
// Bits 3-0 of the 15 bit universe number are encoded into the low nibble
|
||||
// This is used in combination with SubSwitch and NetSwitch to produce the full universe address.
|
||||
// THIS IS FOR INPUT - ART-NET or DMX
|
||||
// NB ON ART-NET II THESE 4 UNIVERSES WILL BE UNICAST TO.
|
||||
|
||||
uint8_t SwOut[4];
|
||||
// Bits 3-0 of the 15 bit universe number are encoded into the low nibble
|
||||
// This is used in combination with SubSwitch and NetSwitch to produce the full universe address.
|
||||
// data belongs
|
||||
// THIS IS FOR OUTPUT - ART-NET or DMX.
|
||||
// NB ON ART-NET II THESE 4 UNIVERSES WILL BE UNICAST TO.
|
||||
|
||||
uint8_t SwVideo;
|
||||
// Low nibble is the value of the video
|
||||
// output channel
|
||||
|
||||
uint8_t SwMacro;
|
||||
// Bit 0 is Macro input 1
|
||||
// Bit 7 is Macro input 8
|
||||
|
||||
uint8_t SwRemote;
|
||||
// Bit 0 is Remote input 1
|
||||
// Bit 7 is Remote input 8
|
||||
|
||||
uint8_t Spare1; // Spare, currently zero
|
||||
uint8_t Spare2; // Spare, currently zero
|
||||
uint8_t Spare3; // Spare, currently zero
|
||||
uint8_t Style; // Set to Style code to describe type of equipment
|
||||
|
||||
uint8_t Mac[6]; // Mac Address, zero if info not available
|
||||
|
||||
uint8_t BindIp[4]; // If this unit is part of a larger or modular product, this is the IP of the
|
||||
// root device.
|
||||
uint8_t BindIndex; // Set to zero if no binding, otherwise this number represents the order of
|
||||
// bound devices. A lower number means closer to root device.
|
||||
|
||||
uint8_t Status2;
|
||||
// bit 0 = 0 Node does not support web browser
|
||||
// bit 0 = 1 Node supports web browser configuration
|
||||
|
||||
// bit 1 = 0 Node's IP address is manually configured
|
||||
// bit 1 = 1 Node's IP address is DHCP configured
|
||||
|
||||
// bit 2 = 0 Node is not DHCP capable
|
||||
// bit 2 = 1 Node is DHCP capable
|
||||
|
||||
// bit 2-7 not implemented, transmit as zero
|
||||
|
||||
uint8_t Filler[26]; // Filler bytes, currently zero.
|
||||
} artPollReply_t;
|
||||
|
||||
static artPollReply_t artPollReply = {
|
||||
.ID = "Art-Net",
|
||||
.OpCode = OpPollReply,
|
||||
.IPAddr = {0},
|
||||
.Port = ARTNET_PORT,
|
||||
.VersionInfo = 1,
|
||||
.NetSwitch = 0,
|
||||
.SubSwitch = 0,
|
||||
.Oem = 0x0190,
|
||||
.UbeaVersion = 0,
|
||||
.Status = 0,
|
||||
.EstaMan = {0, 0},
|
||||
.ShortName = "Tropicananass",
|
||||
.LongName = "Tropicananass Artnetnode",
|
||||
.NodeReport = {0},
|
||||
.NumPortsHi = 0,
|
||||
.NumPortsLo = 1,
|
||||
.PortTypes = {0x80, 0, 0, 0},
|
||||
.GoodInput = {0},
|
||||
.GoodOutput = {0},
|
||||
.SwIn = {0},
|
||||
.SwOut = {0},
|
||||
.SwVideo = 0,
|
||||
.SwMacro = 0,
|
||||
.SwRemote = 0,
|
||||
.Spare1 = 0,
|
||||
.Spare2 = 0,
|
||||
.Spare3 = 0,
|
||||
.Style = 0,
|
||||
.Mac = {0},
|
||||
.BindIp = {0},
|
||||
.BindIndex = 0,
|
||||
.Status2 = 0b00000000,
|
||||
.Filler = {0},
|
||||
};
|
||||
|
||||
#endif // __RPI_ARTNET_PACKETS_H__
|
25
RpiLedBars/src/tasks/artnet/rpi_artnet_utils.h
Normal file
25
RpiLedBars/src/tasks/artnet/rpi_artnet_utils.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#if !defined(__RPI_ARTNET_UTILS_H__)
|
||||
#define __RPI_ARTNET_UTILS_H__
|
||||
|
||||
#define ARTNET_PORT 0x1936
|
||||
|
||||
// Buffers
|
||||
#define ARTNET_MAX_BUFFER 530
|
||||
#define DMX_MAX_BUFFER 512
|
||||
|
||||
// Packet constants
|
||||
#define ARTNET_ID "Art-Net"
|
||||
#define ARTNET_DMX_START_LOC 18
|
||||
|
||||
// Packet confines
|
||||
#define ARTNET_SHORT_NAME_MAX_LENGTH 17
|
||||
#define ARTNET_LONG_NAME_MAX_LENGTH 63
|
||||
|
||||
// DMX settings
|
||||
#define DMX_MAX_OUTPUTS 4
|
||||
#define DMX_MS_BETWEEN_TICKS 25
|
||||
|
||||
// RDM
|
||||
#define DMX_RDM_STARTCODE 0xCC
|
||||
|
||||
#endif // __RPI_ARTNET_UTILS_H__
|
146
RpiLedBars/src/tasks/cava/rpi_cava.c
Normal file
146
RpiLedBars/src/tasks/cava/rpi_cava.c
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "rpi_cava.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define MAXLINECHAR 128 * (5 + 1) + 1
|
||||
|
||||
pid_t cavaPid;
|
||||
bool isFifoReaderRunning = false;
|
||||
pthread_t fifoReader;
|
||||
int cavaFifo;
|
||||
char lineBuffer[MAXLINECHAR];
|
||||
uint16_t buffer[128 + 2];
|
||||
|
||||
static void *fifo_to_buffer(void *arg);
|
||||
|
||||
void setup_cava() {
|
||||
if ((cavaPid = fork()) == -1) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (cavaPid == 0) {
|
||||
/* Child process*/
|
||||
pthread_setschedprio(pthread_self(), 30);
|
||||
char *args[] = {"/usr/local/bin/cava", "-p", "/home/pi/LedBars/RpiLedBars/cava_config", NULL};
|
||||
|
||||
if (execv(args[0], args) != 0) {
|
||||
perror("execv");
|
||||
}
|
||||
|
||||
} else {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
void close_cava() {
|
||||
stop_cava_bg_worker();
|
||||
kill(cavaPid, SIGTERM);
|
||||
}
|
||||
|
||||
void start_cava_bg_worker() {
|
||||
isFifoReaderRunning = true;
|
||||
pthread_create(&fifoReader, NULL, fifo_to_buffer, NULL);
|
||||
}
|
||||
|
||||
void stop_cava_bg_worker() {
|
||||
isFifoReaderRunning = false;
|
||||
if (pthread_join(fifoReader, NULL) != 0) {
|
||||
perror("pthread_join");
|
||||
}
|
||||
}
|
||||
|
||||
int get_cava_buffer(uint16_t **buffer_dst) {
|
||||
*buffer_dst = buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t valueIndex = 0, charOffset = 0;
|
||||
char strValue[6] = "0\0";
|
||||
bool hasToBeDiscarded = false;
|
||||
|
||||
static void *fifo_to_buffer(void *arg) {
|
||||
struct pollfd fds[1];
|
||||
int timeoutMs;
|
||||
int ret;
|
||||
|
||||
if ((cavaFifo = open("/tmp/cava_output", O_RDONLY | O_NONBLOCK)) < 0) {
|
||||
perror("open");
|
||||
close_cava();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(fds, 0, sizeof(fds));
|
||||
fds[0].fd = cavaFifo;
|
||||
fds[0].events = POLLIN;
|
||||
timeoutMs = 10;
|
||||
|
||||
hasToBeDiscarded = true;
|
||||
|
||||
while (isFifoReaderRunning) {
|
||||
|
||||
if ((ret = poll(fds, 1, timeoutMs)) == 1) {
|
||||
int nread;
|
||||
nread = read(cavaFifo, lineBuffer, 128 + 1);
|
||||
|
||||
if (nread >= 0) {
|
||||
for (size_t i = 0; i < nread; ++i) {
|
||||
char current = lineBuffer[i];
|
||||
|
||||
if (hasToBeDiscarded) {
|
||||
if (current == '\n') {
|
||||
charOffset = 0;
|
||||
strValue[charOffset] = '\0';
|
||||
valueIndex = 0;
|
||||
hasToBeDiscarded = false;
|
||||
}
|
||||
|
||||
} else {
|
||||
if ('0' <= current && current <= '9') {
|
||||
strValue[charOffset++] = current;
|
||||
} else if (current == '\n' || current == ';') {
|
||||
strValue[charOffset] = '\0';
|
||||
charOffset = 0;
|
||||
buffer[valueIndex++] = atoi(strValue);
|
||||
|
||||
if (current == '\n' || valueIndex > 129) {
|
||||
valueIndex = 0;
|
||||
|
||||
if (valueIndex > 129) {
|
||||
fprintf(stderr, "Buffer overflow, \\n missed, discarding next\n");
|
||||
hasToBeDiscarded = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "Unexpected char %d [%c]\n", current, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (errno != EAGAIN) {
|
||||
perror("read");
|
||||
}
|
||||
}
|
||||
} else if (ret < 0) {
|
||||
fprintf(stderr, "error polling %d: %s\n", cavaFifo, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
close(cavaFifo);
|
||||
|
||||
return NULL;
|
||||
}
|
16
RpiLedBars/src/tasks/cava/rpi_cava.h
Normal file
16
RpiLedBars/src/tasks/cava/rpi_cava.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#if !defined(__RPI_CAVA_H__)
|
||||
#define __RPI_CAVA_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void setup_cava();
|
||||
|
||||
int get_cava_buffer(uint16_t **buffer_dst);
|
||||
|
||||
void close_cava();
|
||||
|
||||
void start_cava_bg_worker();
|
||||
|
||||
void stop_cava_bg_worker();
|
||||
|
||||
#endif /* __RPI_CAVA_H__ */
|
Reference in New Issue
Block a user