cava with pipe, and template

This commit is contained in:
Nathan GOUARDERES 2021-07-30 10:59:34 +02:00
parent 7117cbbe4c
commit 6add75c64e
12 changed files with 378 additions and 85 deletions

View File

@ -12,7 +12,7 @@ source = plughw:1
method = raw
channels = mono
mono_option = left
raw_target = /tmp/cava_output
;raw_target = /dev/stdout
data_format = ascii
ascii_max_range=65535
bit_format = 16bit

View File

@ -0,0 +1,25 @@
[general]
framerate = 60
autosens = 0
sensitivity = 200
bars = 128
[input]
method = alsa
source = plughw:1
[output]
method = raw
channels = mono
mono_option = left
raw_target = /tmp/cava_output
data_format = ascii
ascii_max_range=65535
bit_format = 16bit
[smoothing]
integral = 0
monstercat = 1
waves = 0
gravity = 0

View File

@ -1,5 +1,6 @@
#include "rpi_gpio.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
@ -17,6 +18,8 @@
#define GPIO_MODE_STRS "IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"
bool isInitialized = false;
// Virtual memory pointers to acceess GPIO, DMA and PWM from user space
MEM_MAP gpio_regs;
@ -24,9 +27,19 @@ char *gpio_mode_strs[] = {GPIO_MODE_STRS};
// definitions
void gpio_setup() { map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE); }
void gpio_setup() {
if (!isInitialized) {
map_periph(&gpio_regs, (void *)GPIO_BASE, PAGE_SIZE);
isInitialized = true;
}
}
void gpio_close() { unmap_periph_mem(&gpio_regs); }
void gpio_close() {
if (isInitialized) {
unmap_periph_mem(&gpio_regs);
isInitialized = true;
}
}
// Set input or output with pullups
void gpio_set(int pin, int mode, int pull) {

View File

@ -1,11 +1,37 @@
/** \file rpi_selector.c
* \brief This module is used to manage selector on gpio
*/
/***************************************************************************************************
* Includes
**************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
/***************************************************************************************************
* Preprocessor Constants and Macros
**************************************************************************************************/
/***************************************************************************************************
* Type and Contant Definitions
**************************************************************************************************/
unsigned const selectorPinNumber = 4;
/* TODO use GPIO function from ../gpio/gipo.h (same pin number) */
int selectorPins[4] = {5, 6, 26, 27};
void setup_selector() {
/***************************************************************************************************
* Persistent Variables
**************************************************************************************************/
/***************************************************************************************************
* Internal Function Prototypes
**************************************************************************************************/
/***************************************************************************************************
* External Function Definitions
**************************************************************************************************/
void selector_setup() {
wiringPiSetupGpio();
for (size_t i = 0; i < selectorPinNumber; ++i) {
@ -14,7 +40,7 @@ void setup_selector() {
}
}
int get_selector_position() {
int selector_get_position() {
for (size_t i = 0; i < selectorPinNumber; ++i) {
if (digitalRead(selectorPins[i])) {
return i;
@ -23,10 +49,14 @@ int get_selector_position() {
return -1;
}
void print_selector() {
void selector_print() {
char modeStr[] = "0 | 0 | 0 | 0 \n";
for (size_t i = 0; i < selectorPinNumber; ++i) {
modeStr[i * 4] = digitalRead(selectorPins[i]) ? '1' : '0';
}
printf(modeStr);
}
fprintf(stderr, modeStr);
}
/***************************************************************************************************
* Internal Function Definitions
**************************************************************************************************/

View File

@ -1,10 +1,40 @@
/** \file rpi_selector.h
* \brief This module is used to manage selector on gpio
*/
/***************************************************************************************************
* Includes
**************************************************************************************************/
#if !defined(__RPI_SELECTOR_H__)
#define __RPI_SELECTOR_H__
void setup_selector();
/***************************************************************************************************
* Preprocessor Constants and Macros
**************************************************************************************************/
int get_selector_position();
/***************************************************************************************************
* Type and Contant Definitions
**************************************************************************************************/
void print_selector();
/***************************************************************************************************
* Global Variables
**************************************************************************************************/
/***************************************************************************************************
* External Function Prototypes
**************************************************************************************************/
/**
* \brief setup the selector module
**/
void selector_setup();
/**
* \brief get the selector position
**/
int selector_get_position();
/**
* \brief debug: print the selector position
**/
void selector_print();
#endif /* __RPI_SELECTOR_H__ */

View File

@ -49,18 +49,10 @@ int main(int argc, char const *argv[]) {
fprintf(stderr, "WARNING: Failed to set stepper thread to real-time priority\n");
}
setup_selector();
// // setup led
// map_devices();
// init_smi(LED_NCHANS, SMI_TIMING);
// map_uncached_mem(&vc_mem, VC_MEM_SIZE);
// setup_smi_dma(&vc_mem, TX_BUFF_LEN(chanLedCount), &txdata);
midi_controller_setup();
selector_setup();
leddriver_setup();
setup_midi_controller();
artnet_init();
setup_cava();
@ -68,8 +60,8 @@ int main(int argc, char const *argv[]) {
while (1) {
struct timespec loopStart;
clock_gettime(CLOCK_MONOTONIC, &loopStart);
int mode = get_selector_position();
execute_midi_controller();
int mode = selector_get_position();
midi_controller_execute();
if (mode != -1) {
if (mode != previousMode) {
@ -79,7 +71,7 @@ int main(int argc, char const *argv[]) {
/* stop previous bg task */
switch (previousMode) {
case 1:
stop_artnet_bg_worker();
artnet_stop();
break;
case 2:
stop_cava_bg_worker();
@ -91,7 +83,7 @@ int main(int argc, char const *argv[]) {
/* start new bg task */
switch (mode) {
case 1:
start_artnet_bg_worker();
artnet_start();
break;
case 2:
start_cava_bg_worker();

View File

@ -14,7 +14,7 @@ void handle_system_port_events(snd_seq_event_t *ev);
void handle_in_port_events(snd_seq_event_t *ev);
void setup_midi_controller() {
void midi_controller_setup() {
if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK) != 0) {
SNDERR("snd_seq_open");
}
@ -45,7 +45,7 @@ void setup_midi_controller() {
subscribe_midi_controller(controller);
}
void execute_midi_controller() {
void midi_controller_execute() {
snd_seq_event_t *ev = NULL;
int ret;
while ((ret = snd_seq_event_input(seq_handle, &ev)) > 0) {
@ -63,7 +63,7 @@ void execute_midi_controller() {
}
}
void close_midi_controller() {}
void midi_controller_close() {}
void subscribe_system_port() {
snd_seq_addr_t sender = {.client = 0, .port = SND_SEQ_PORT_SYSTEM_ANNOUNCE};

View File

@ -1,10 +1,10 @@
#if !defined(__RPI_MIDI_CONTROLLER_H__)
#define __RPI_MIDI_CONTROLLER_H__
void setup_midi_controller();
void midi_controller_setup();
void execute_midi_controller();
void midi_controller_execute();
void close_midi_controller();
void midi_controller_close();
#endif /* __RPI_MIDI_CONTROLLER_H__ */

View File

@ -1,3 +1,12 @@
/** \file rpi_artnet.c
* \brief This module contains continuous tasks for artnet node communications.
*
* This is the implementation file.
*/
/***************************************************************************************************
* Includes
**************************************************************************************************/
#include "rpi_artnet.h"
#include <arpa/inet.h>
@ -13,19 +22,57 @@
#include "rpi_artnet_utils.h"
int udpSocket = -1;
char buffer[1024];
/***************************************************************************************************
* Preprocessor Constants and Macros
**************************************************************************************************/
/***************************************************************************************************
* Type and Contant Definitions
**************************************************************************************************/
/***************************************************************************************************
* Persistent Variables
**************************************************************************************************/
/**
* Boolean for interrupting the main thread loop
*/
bool isUdpListenerRunning = false;
/**
* Task thread identifier
*/
pthread_t udpListener;
/**
* Buffer for storing artnet dmx data received
*/
uint8_t artDmxBufferArray[16][512];
/***************************************************************************************************
* Internal Function Prototypes
**************************************************************************************************/
/**
* \brief This function is used to thread main execution function
*
* \param arg not used.
*
* \return NULL.
*/
static void *artnet_udp_handler(void *arg);
void artnet_send_poll_reply(struct sockaddr_in srcAddr);
/**
* \brief This function is used to send an artnet poll reply packet
*
* \param[in] fdUdpSocket The UDP socket file descriptor.
*
* \param[in] senderAddr The adress of poll emiter.
*/
static void artnet_send_poll_reply(int fdUdpSocket, struct sockaddr_in senderAddr);
void artnet_init() {}
/***************************************************************************************************
* External Function Definitions
**************************************************************************************************/
void start_artnet_bg_worker() {
void artnet_start() {
isUdpListenerRunning = true;
if (pthread_create(&udpListener, NULL, artnet_udp_handler, NULL) < 0) {
@ -33,7 +80,7 @@ void start_artnet_bg_worker() {
}
}
void stop_artnet_bg_worker() {
void artnet_stop() {
isUdpListenerRunning = false;
if (pthread_join(udpListener, NULL) != 0) {
@ -41,7 +88,7 @@ void stop_artnet_bg_worker() {
}
}
int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData) {
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;
@ -51,27 +98,32 @@ int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData) {
return 0;
}
/***************************************************************************************************
* Internal Function Definitions
**************************************************************************************************/
static void *artnet_udp_handler(void *arg) {
struct sockaddr_in serverAddr;
struct pollfd fds[1];
int fdUdpSocket = -1;
struct pollfd pollFdArray[1];
int timeoutMs;
int ret;
int flags;
char buffer[ARTNET_MAX_BUFFER];
struct sockaddr_in serverAddr, srcAddr;
socklen_t srcLen = sizeof(struct sockaddr_in);
/* Create UDP socket */
udpSocket = socket(PF_INET, SOCK_DGRAM, 0);
if (udpSocket < 0) {
fdUdpSocket = socket(PF_INET, SOCK_DGRAM, 0);
if (fdUdpSocket < 0) {
perror("Opening socket failed");
}
/* Set non-blocking socket */
flags = fcntl(udpSocket, F_GETFL, 0);
fcntl(udpSocket, F_SETFL, flags | O_NONBLOCK);
flags = fcntl(fdUdpSocket, F_GETFL, 0);
fcntl(fdUdpSocket, F_SETFL, flags | O_NONBLOCK);
/* pollfd structure and timeout */
memset(fds, 0, sizeof(fds));
fds[0].fd = udpSocket;
fds[0].events = POLLIN;
memset(pollFdArray, 0, sizeof(pollFdArray));
pollFdArray[0].fd = fdUdpSocket;
pollFdArray[0].events = POLLIN;
timeoutMs = 10;
/* Configure settings in address struct */
@ -81,16 +133,14 @@ static void *artnet_udp_handler(void *arg) {
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);
bind(fdUdpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
while (isUdpListenerRunning) {
if ((ret = poll(fds, 1, timeoutMs)) == 1) {
int pollReturn;
if ((pollReturn = poll(pollFdArray, 1, timeoutMs)) == 1) {
ssize_t bufferLen =
recvfrom(udpSocket, buffer, ARTNET_MAX_BUFFER, 0, (struct sockaddr *)&srcAddr, &srcLen);
recvfrom(fdUdpSocket, buffer, ARTNET_MAX_BUFFER, 0, (struct sockaddr *)&srcAddr, &srcLen);
if (bufferLen <= ARTNET_MAX_BUFFER && bufferLen > sizeof(artnetHeader_t)) {
artnetHeader_t *artnetHeader = (artnetHeader_t *)buffer;
@ -110,7 +160,7 @@ static void *artnet_udp_handler(void *arg) {
break;
case OpPoll:
artnet_send_poll_reply(srcAddr);
artnet_send_poll_reply(fdUdpSocket, srcAddr);
break;
default:
@ -118,22 +168,26 @@ static void *artnet_udp_handler(void *arg) {
}
}
}
} else if (ret < 0) {
fprintf(stderr, "error polling %d: %s\n", udpSocket, strerror(errno));
} else if (pollReturn < 0) {
fprintf(stderr, "error polling %d: %s\n", fdUdpSocket, strerror(errno));
}
}
close(udpSocket);
close(fdUdpSocket);
return NULL;
}
void artnet_send_poll_reply(struct sockaddr_in srcAddr) {
static void artnet_send_poll_reply(int fdUdpSocket, struct sockaddr_in senderAddr) {
/* 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));
senderAddr.sin_family = AF_INET;
senderAddr.sin_port = htons(ARTNET_PORT);
memset(senderAddr.sin_zero, '\0', sizeof(senderAddr.sin_zero));
sendto(udpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0,
(struct sockaddr *)&srcAddr, sizeof(srcAddr));
if (sendto(fdUdpSocket, (uint8_t *)&artPollReply, sizeof(artPollReply_t), 0,
(struct sockaddr *)&senderAddr, sizeof(senderAddr)) < 0) {
fprintf(stderr, "Error sending poll reply to %s:%d: %s\n", inet_ntoa(senderAddr.sin_addr),
strerror(errno));
}
}

View File

@ -1,14 +1,50 @@
/** \file rpi_artnet.h
* \brief This module contains continuous tasks for artnet node communications.
*
* This is the header file for the definition of services to allow managing thread acting as artnet
* node.
*/
#if !defined(__RPI_ARTNET_H__)
#define __RPI_ARTNET_H__
/***************************************************************************************************
* Includes
**************************************************************************************************/
#include "rpi_artnet_packets.h"
void artnet_init();
/***************************************************************************************************
* Preprocessor Constants and Macros
**************************************************************************************************/
void start_artnet_bg_worker();
/***************************************************************************************************
* Type and Contant Definitions
**************************************************************************************************/
void stop_artnet_bg_worker();
/***************************************************************************************************
* Global Variables
**************************************************************************************************/
int artnet_get_dmx_data(unsigned int univerve, uint8_t **dmxData);
/***************************************************************************************************
* External Function Prototypes
**************************************************************************************************/
#endif // __RPI_ARTNET_H__
/**
* \brief Start artnet node module
**/
void artnet_start();
/**
* \brief Stop artnet node module
*/
void artnet_stop();
/**
* \brief Get last DMX data received for the specified universe
*
* \param[in] univerve The universe to get data from
*
* \param[out] dmxData The pointer to the DMX data array
*/
int artnet_get_dmx_data(unsigned int univerve, uint8_t *dmxData[]);
#endif /* __RPI_ARTNET_H__ */

View File

@ -1,3 +1,10 @@
/** \file rpi_cava.h
* \brief This module contains continuous tasks for cava (spectrum analyzer) comunication
*/
/***************************************************************************************************
* Includes
**************************************************************************************************/
#include "rpi_cava.h"
#include <errno.h>
@ -9,23 +16,59 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <Windows.h>
#else
#include <unistd.h>
#endif
/***************************************************************************************************
* Preprocessor Constants and Macros
**************************************************************************************************/
/**
* Constant for Maximum char in a line (128 number of (5 char + 1 delim) + \n )
*/
#define MAXLINECHAR 128 * (5 + 1) + 1
/***************************************************************************************************
* Type and Contant Definitions
**************************************************************************************************/
/***************************************************************************************************
* Persistent Variables
**************************************************************************************************/
/**
* Cava process id
*/
pid_t cavaPid;
/**
* Boolean for interrupting the main thread loop
*/
bool isFifoReaderRunning = false;
/**
* Task thread identifier
*/
pthread_t fifoReader;
int cavaFifo;
int fdCavaInput;
char lineBuffer[MAXLINECHAR];
uint16_t buffer[128 + 2];
/***************************************************************************************************
* Internal Function Prototypes
**************************************************************************************************/
/**
* \brief This function is used to thread main execution function
*
* \param arg not used.
*
* \return NULL.
*/
static void *fifo_to_buffer(void *arg);
/***************************************************************************************************
* External Function Definitions
**************************************************************************************************/
void setup_cava() {
if ((cavaPid = fork()) == -1) {
perror("fork");
@ -34,7 +77,6 @@ void setup_cava() {
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) {
@ -68,33 +110,36 @@ int get_cava_buffer(uint16_t **buffer_dst) {
return 0;
}
size_t valueIndex = 0, charOffset = 0;
char strValue[6] = "0\0";
bool hasToBeDiscarded = false;
/***************************************************************************************************
* Internal Function Definitions
**************************************************************************************************/
static void *fifo_to_buffer(void *arg) {
struct pollfd fds[1];
int timeoutMs;
int ret;
size_t valueIndex = 0, charOffset = 0;
char strValue[6] = "0\0";
bool hasToBeDiscarded = true;
if ((cavaFifo = open("/tmp/cava_output", O_RDONLY | O_NONBLOCK)) < 0) {
if start_cava_process ()
;
if ((fdCavaInput = 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].fd = fdCavaInput;
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);
nread = read(fdCavaInput, lineBuffer, 128 + 1);
if (nread >= 0) {
for (size_t i = 0; i < nread; ++i) {
@ -136,11 +181,54 @@ static void *fifo_to_buffer(void *arg) {
}
}
} else if (ret < 0) {
fprintf(stderr, "error polling %d: %s\n", cavaFifo, strerror(errno));
fprintf(stderr, "error polling %d: %s\n", fdCavaInput, strerror(errno));
}
}
close(cavaFifo);
close(fdCavaInput);
return NULL;
}
static int start_cava_process() {
int fdCavaPipe[2];
if (pipe(fdCavaPipe) < 0) {
fprintf(stderr, "Cava pipe failure: %s\n", strerror(errno));
return -EXIT_FAILURE;
}
if ((cavaPid = fork()) < 0) {
fprintf(stderr, "Cava fork failure: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (cavaPid == 0) {
/* Child process*/
char *args[] = {"cava", "-p", "/home/pi/LedBars/RpiLedBars/cava_config", NULL};
/* Close reading end of the pipe */
close(fdCavaPipe[0]);
/* Dup writing end of the pipe in place of stdout */
dup2(fdCavaPipe[1], STDOUT_FILENO);
/* Close writing end of the pipe */
close(fdCavaPipe[1]);
execvp(args[0], args);
fprintf(stderr, "Cava execvp failure or return: %s\n", strerror(errno));
exit(EXIT_FAILURE);
} else {
/* Close writing end of the pipe */
close(fdCavaPipe[1]);
/* Set reading end of the pipe non-blocking */
flags = fcntl(fdCavaPipe[0], F_GETFL, 0);
fcntl(fdCavaPipe[0], F_SETFL, flags | O_NONBLOCK);
/* Return reading end of the pipe */
return fdCavaPipe[0];
}
/* Unreachable */
return -1;
}
static void stop_cava_process() { kill(cavaPid, SIGTERM); }

View File

@ -1,8 +1,33 @@
/** \file rpi_cava.h
* \brief This module contains continuous tasks for cava (spectrum analyzer) comunication
*/
#if !defined(__RPI_CAVA_H__)
#define __RPI_CAVA_H__
/***************************************************************************************************
* Includes
**************************************************************************************************/
#include <stdint.h>
/***************************************************************************************************
* Preprocessor Constants and Macros
**************************************************************************************************/
/***************************************************************************************************
* Type and Contant Definitions
**************************************************************************************************/
/***************************************************************************************************
* Global Variables
**************************************************************************************************/
/***************************************************************************************************
* External Function Prototypes
**************************************************************************************************/
/**
* \brief Start artnet node module
**/
void setup_cava();
int get_cava_buffer(uint16_t **buffer_dst);