soundcheck wrote:
>
> Have you ever considered to build a FIFO/pipe output into squeezelite???
> (I mentioned that some time back)
> This way squeezelite could be piped into e.g. brutefir which could
> handle crossovers for active speakers, equalization filters and channel
> timing/phase adjustments and optionally convolution.
>
There is better solution than pipe/stdout, use alsa loopback between
squeezelite and brutefir, below a controlling application to handle
(re)starting squeezelite/brutefir and use the output card only when
playing (enables Kodi using same output card).
Squeezelite does up/downsampling using sox to 96kHz/24bits, so brutefir
can have a fixed configuration. Only downside that it will need quite a
lot processing power with high quality setting, but works very well with
cubox-i-4pro that I am using.
The block diagram of data flow:
17960
sqbrctl source code sqbrctl.c:
Code:
--------------------
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sched.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <arpa/inet.h>
int room_correction = 1;
int upsample = 1;
typedef struct {
char *key;
char *value;
int *parameter;
int setvalue;
} config_t;
#define CONFS 4
config_t confs[CONFS] = {
{ "roomcorrection", "off", &room_correction, 0 },
{ "roomcorrection", "on" , &room_correction, 1 },
{ "upsample", "off", &upsample, 0 },
{ "upsample", "on" , &upsample, 1 },
};
void read_sqbrctl_conf(void)
{
FILE *f;
char key[4096];
char *value;
int i;
f = fopen("/etc/drc/sqbrctl.conf", "r");
if (f == NULL) return;
/* read key/value pairs */
while (!feof(f)) {
if (fgets(key, sizeof(key), f) != NULL) {
value = strstr(key, ":");
if (value == NULL) continue;
value++;
for (i = 0; i < CONFS; i++) {
if (strncmp(key,confs[i].key,strlen(confs[i].key)) == 0) {
if (strncmp(value,confs[i].value, strlen(confs[i].value)) == 0) {
*confs[i].parameter = confs[i].setvalue;
}
}
}
}
}
fclose(f);
}
int set_rt_priority(int priority)
{
struct sched_param schp;
memset(&schp, 0, sizeof(schp));
schp.sched_priority = priority;
if (sched_setscheduler(0, SCHED_FIFO, &schp) != 0) {
return errno;
}
return 0;
}
int need_reload;
void signal_callback_handler(int signum)
{
need_reload = 1;
return;
}
pid_t stop_brutefir(pid_t previous)
{
if (previous != 0) {
system("killall brutefir");
waitpid(previous, NULL, 0);
}
return 0;
}
pid_t start_brutefir(pid_t previous)
{
pid_t pid;
static char *argv[]={"brutefir","/etc/drc/brutefir.conf",NULL};
stop_brutefir(previous);
pid=fork();
if (pid == 0) {
/* child process */
execv("/usr/bin/brutefir",argv);
exit(0); /* only if execv fails */
} else if (pid > 0) {
return pid;
} else {
return 0;
}
}
typedef enum { SQLITE_LOOP_PLAY, SQLITE_DIRECT_PLAY } sqlite_mode_e;
pid_t stop_squeezelite(pid_t previous)
{
if (previous != 0) {
system("killall squeezelite");
waitpid(previous, NULL, 0);
}
return 0;
}
pid_t start_squeezelite(pid_t previous, sqlite_mode_e sqlite_mode)
{
pid_t pid;
static char *argv_direct_upsample[]=
{
"squeezelite",
"-n", "soa-sql-kjh",
"-p", "9",
"-r", "96000-96000",
"-a", "200:128:32",
"-u", "vLs:18:3",
"-C", "2",
"-o", "front:CARD=D20,DEV=0",
NULL
};
static char *argv_direct_noupsample[]=
{
"squeezelite",
"-n", "soa-sql-kjh",
"-p", "9",
"-a", "200:128:32",
"-C", "2",
"-o", "front:CARD=D20,DEV=0",
NULL
};
static char *argv_loop_upsample[]=
{
"squeezelite",
"-n", "soa-sql-kjh",
"-p", "9",
"-r", "96000-96000",
"-a", "200:128:32",
"-u", "vLs:18:3",
"-C", "2",
"-o", "hw:Loopback,1,0",
NULL
};
static char *argv_loop_noupsample[]=
{
"squeezelite",
"-n", "soa-sql-kjh",
"-p", "9",
"-a", "200:128:32",
"-C", "2",
"-o", "hw:Loopback,1,0",
NULL
};
stop_squeezelite(previous);
pid=fork();
if (pid == 0) {
/* child process */
if (sqlite_mode == SQLITE_DIRECT_PLAY) {
if (upsample) {
execv("/usr/bin/squeezelite", argv_direct_upsample);
} else {
execv("/usr/bin/squeezelite", argv_direct_noupsample);
}
} else {
if (upsample) {
execv("/usr/bin/squeezelite", argv_loop_upsample);
} else {
execv("/usr/bin/squeezelite", argv_loop_noupsample);
}
}
exit(0); /* only if execv fails */
} else if (pid > 0 ) {
return pid;
} else {
return 0;
}
}
#define BUFFER_SIZE (4096)
int sqlite_playing_loop(void)
{
int ret = 0;
char buffer[BUFFER_SIZE];
int fd = open("/proc/asound/Loopback/cable#1",O_RDONLY);
if (fd < 0) return ret;
if (read(fd, buffer, BUFFER_SIZE) > 0) {
if (strstr(buffer, "Playback") != NULL) {
ret = 1;
}
}
close(fd);
return ret;
}
int sqlite_playing_direct(void)
{
int ret = 0;
char buffer[BUFFER_SIZE];
int fd = open("/proc/asound/D20/pcm0p/sub0/hw_params",O_RDONLY);
if (fd < 0) return ret;
if (read(fd, buffer, BUFFER_SIZE) > 0) {
if (strstr(buffer, "access") != NULL) {
ret = 1;
}
}
close(fd);
return ret;
}
#define SLEEP_TIME (1000*1000)
int main(int argc, char *argv[])
{
int run_mode = 1;
pid_t squeezelite = 0;
pid_t brutefir = 0;
int st;
int sqlite_play;
pid_t exited;
int loops = 0;
usleep(20*1000*1000);
close(2);
close(1);
close(0);
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
open("/dev/null", O_WRONLY);
signal(SIGHUP, signal_callback_handler);
set_rt_priority(10);
need_reload = 1;
for (;;) {
if (need_reload) {
/* run at start, when play stopped or signal HUP send to sqbrctl */
need_reload = 0;
run_mode = 1;
brutefir = stop_brutefir(brutefir);
read_sqbrctl_conf();
squeezelite = start_squeezelite(squeezelite,SQLITE_LOOP_PLAY);
}
usleep(SLEEP_TIME);
if ((run_mode == 2) && (room_correction == 0)) {
sqlite_play = sqlite_playing_direct();
if (sqlite_play == 0) need_reload = 1;
}
if ((run_mode == 1) || (room_correction)) {
sqlite_play = sqlite_playing_loop();
if ((run_mode == 1) && (sqlite_play != 0)) {
run_mode = 2;
if (room_correction) {
brutefir = start_brutefir(brutefir);
} else {
squeezelite =
start_squeezelite(squeezelite,SQLITE_DIRECT_PLAY);
}
}
}
if ((run_mode == 2) && (room_correction) && (sqlite_play == 0)) need_reload =
1;
if (++loops > 10) {
loops = 0;
exited = waitpid(-1, &st, WNOHANG);
if (WIFEXITED(st)) {
if (exited == squeezelite) {
squeezelite = start_squeezelite(0,SQLITE_LOOP_PLAY);
}
if (exited == brutefir) {
if (run_mode == 2) {
brutefir = start_brutefir(0);
}
}
}
}
}
return 0;
}
--------------------
Brutefir config file /etc/drc/brutefir.conf:
Code:
--------------------
## DEFAULT GENERAL SETTINGS ##
float_bits: 64; # internal floating point precision
sampling_rate: 96000; # sampling rate in Hz of audio interfaces
filter_length: 32768; # length of filters
overflow_warnings: false; # echo warnings to stderr if overflow occurs
show_progress: false; # echo filtering progress to stderr
max_dither_table_size: 0; # maximum size in bytes of precalculated dither
allow_poll_mode: false; # allow use of input poll mode
modules_path: "/usr/lib/"; # extra path where to find BruteFIR modules
monitor_rate: false; # monitor sample rate
powersave: false; # pause filtering when input is zero
lock_memory: true; # try to lock memory if realtime prio is set
sdf_length: 63; # subsample filter length
convolver_config: "/etc/drc/convolver_config"; # location of convolver config
file
coeff "drc_l" {
filename: "/etc/drc/filter-l.pcm";
format: "FLOAT_LE"; # file format
attenuation: 5.0; # attenuation in dB
shared_mem: true;
};
coeff "drc_r" {
filename: "/etc/drc/filter-r.pcm";
format: "FLOAT_LE"; # file format
attenuation: 5.0; # attenuation in dB
shared_mem: true;
};
## INPUT DEFAULTS ##
# module and parameters to get audio
input "left_in", "right_in" {
device: "alsa" { device: "hw:Loopback,0,0"; link: false; };
sample: "S32_LE";
channels: 2;
};
## OUTPUT DEFAULTS ##
output "left_out", "right_out" {
device: "alsa" { device: "front:CARD=D20,DEV=0"; link: false; ignore_xrun:
true; };
sample: "S32_LE";
channels: 2;
dither: true;
};
## FILTER DEFAULTS ##
filter "l_filter" {
from_inputs: "left_in"/5.0;
to_outputs: "left_out"/0.0;
coeff: "drc_l";
};
filter "r_filter" {
from_inputs: "right_in"/5.0;
to_outputs: "right_out"/0.0;
coeff: "drc_r";
};
--------------------
sqbrctl config file /erc/drc/sqbrctl.conf:
Code:
--------------------
roomcorrection:on
upsample:on
--------------------
Changing drc filters and sqbrctl settings can be done by: rm
/etc/drc/convolver_config; cp /new_fillter_path/filter-*.pcm /etc/drc/;
kill -HUP `pidof sqbrctl`;
That will start current song from beginning and use the updated drc
filters. Upsampling is necessary to be on always when using brutefir,
but without roomccorrection (squeezelite alone) upsample can be set to
off too.
Obviously the ALSA output card needs to be changed both in sqbrctl.c and
brutefir.conf for the card that you are using.
I have tested few months pipe/stdout connections and one month now with
this, and this solution is better both soundwise and stability.
You need to have alsa compiled with snd-aloop module and it loaded in
system startup.
+-------------------------------------------------------------------+
|Filename: drc.png |
|Download: http://forums.slimdevices.com/attachment.php?attachmentid=17960|
+-------------------------------------------------------------------+
------------------------------------------------------------------------
khaho's Profile: http://forums.slimdevices.com/member.php?userid=64334
View this thread: http://forums.slimdevices.com/showthread.php?t=97046
_______________________________________________
unix mailing list
[email protected]
http://lists.slimdevices.com/mailman/listinfo/unix