366 lines
8.2 KiB
C++
366 lines
8.2 KiB
C++
/*
|
|
* bitvis
|
|
* Copyright (C) Bob 2012
|
|
*
|
|
* bitvis 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* bitvis 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "util/inclstdint.h"
|
|
#include "bitvis.h"
|
|
#include "util/log.h"
|
|
#include "util/misc.h"
|
|
#include "util/timeutils.h"
|
|
|
|
#include <signal.h>
|
|
#include <sys/signalfd.h>
|
|
#include <math.h>
|
|
#include <string>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
using namespace std;
|
|
|
|
#define CONNECTINTERVAL 1000000
|
|
|
|
CBitVis::CBitVis(int argc, char *argv[])
|
|
{
|
|
g_printdebuglevel = true;
|
|
m_stop = false;
|
|
m_buf = NULL;
|
|
m_bufsize = 0;
|
|
m_fftbuf = NULL;
|
|
m_displaybuf = NULL;
|
|
m_samplecounter = 0;
|
|
m_nrffts = 0;
|
|
m_peakholds = NULL;
|
|
}
|
|
|
|
CBitVis::~CBitVis()
|
|
{
|
|
}
|
|
|
|
void CBitVis::Setup()
|
|
{
|
|
//init the logfile
|
|
SetLogFile("bitvis.log");
|
|
|
|
SetupSignals();
|
|
|
|
jack_set_error_function(JackError);
|
|
jack_set_info_function(JackInfo);
|
|
}
|
|
|
|
void CBitVis::SetupSignals()
|
|
{
|
|
m_signalfd = -1;
|
|
|
|
sigset_t sigset;
|
|
if (sigemptyset(&sigset) == -1)
|
|
{
|
|
LogError("sigemptyset: %s", GetErrno().c_str());
|
|
return;
|
|
}
|
|
|
|
if (sigaddset(&sigset, SIGTERM) == -1)
|
|
{
|
|
LogError("adding SIGTERM: %s", GetErrno().c_str());
|
|
return;
|
|
}
|
|
|
|
if (sigaddset(&sigset, SIGINT) == -1)
|
|
{
|
|
LogError("adding SIGINT: %s", GetErrno().c_str());
|
|
return;
|
|
}
|
|
|
|
//create a file descriptor that will catch SIGTERM and SIGINT
|
|
m_signalfd = signalfd(-1, &sigset, SFD_NONBLOCK);
|
|
if (m_signalfd == -1)
|
|
{
|
|
LogError("signalfd: %s", GetErrno().c_str());
|
|
}
|
|
else
|
|
{
|
|
//block SIGTERM and SIGINT
|
|
if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1)
|
|
LogError("sigpocmask: %s", GetErrno().c_str());
|
|
}
|
|
|
|
if (sigemptyset(&sigset) == -1)
|
|
{
|
|
LogError("sigemptyset: %s", GetErrno().c_str());
|
|
return;
|
|
}
|
|
|
|
if (sigaddset(&sigset, SIGPIPE) == -1)
|
|
{
|
|
LogError("adding SIGPIPE: %s", GetErrno().c_str());
|
|
return;
|
|
}
|
|
|
|
//libjack throws SIGPIPE a lot, block it
|
|
if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1)
|
|
LogError("sigpocmask: %s", GetErrno().c_str());
|
|
}
|
|
|
|
void CBitVis::Process()
|
|
{
|
|
int64_t lastconnect = GetTimeUs() - CONNECTINTERVAL - 1;
|
|
|
|
while (!m_stop)
|
|
{
|
|
bool didconnect = false;
|
|
|
|
if (m_jackclient.ExitStatus())
|
|
{
|
|
LogError("Jack client exited with code %i reason: \"%s\"",
|
|
(int)m_jackclient.ExitStatus(), m_jackclient.ExitReason().c_str());
|
|
m_jackclient.Disconnect();
|
|
}
|
|
|
|
if (!m_jackclient.IsConnected() && GetTimeUs() - lastconnect > CONNECTINTERVAL)
|
|
{
|
|
m_jackclient.Connect();
|
|
didconnect = true;
|
|
}
|
|
|
|
uint8_t msg;
|
|
while ((msg = m_jackclient.GetMessage()) != MsgNone)
|
|
LogDebug("got message %s from jack client", MsgToString(msg));
|
|
|
|
if (!m_socket.IsOpen() && GetTimeUs() - lastconnect > CONNECTINTERVAL)
|
|
{
|
|
if (m_socket.Open("192.168.88.117", 1337, 60000000) == FAIL)
|
|
{
|
|
LogError("Failed to connect: %s", m_socket.GetError().c_str());
|
|
m_socket.Close();
|
|
}
|
|
else
|
|
{
|
|
Log("Connected");
|
|
}
|
|
didconnect = true;
|
|
}
|
|
|
|
if (didconnect)
|
|
lastconnect = GetTimeUs();
|
|
|
|
if (m_jackclient.IsConnected())
|
|
{
|
|
ProcessAudio();
|
|
}
|
|
else
|
|
{
|
|
sleep(1);
|
|
}
|
|
|
|
ProcessSignalfd();
|
|
}
|
|
|
|
m_jackclient.Disconnect();
|
|
}
|
|
|
|
void CBitVis::ProcessSignalfd()
|
|
{
|
|
signalfd_siginfo siginfo;
|
|
int returnv = read(m_signalfd, &siginfo, sizeof(siginfo));
|
|
if (returnv == -1 && errno != EAGAIN)
|
|
{
|
|
LogError("reading signals fd: %s", GetErrno().c_str());
|
|
if (errno != EINTR)
|
|
{
|
|
close(m_signalfd);
|
|
m_signalfd = -1;
|
|
}
|
|
}
|
|
else if (returnv > 0)
|
|
{
|
|
if (siginfo.ssi_signo == SIGTERM || siginfo.ssi_signo == SIGINT)
|
|
{
|
|
Log("caught %s, exiting", siginfo.ssi_signo == SIGTERM ? "SIGTERM" : "SIGINT");
|
|
m_stop = true;
|
|
}
|
|
else
|
|
{
|
|
LogDebug("caught signal %i", siginfo.ssi_signo);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBitVis::ProcessAudio()
|
|
{
|
|
const int bins = 1024;
|
|
const int lines = 120;
|
|
const float decay = 0.5;
|
|
int samplerate;
|
|
int samples;
|
|
int64_t audiotime;
|
|
if ((samples = m_jackclient.GetAudio(m_buf, m_bufsize, samplerate, audiotime)) > 0)
|
|
{
|
|
m_fft.Allocate(bins * 2);
|
|
if (!m_fftbuf)
|
|
{
|
|
m_fftbuf = new float[bins];
|
|
memset(m_fftbuf, 0, bins * sizeof(float));
|
|
}
|
|
|
|
if (!m_displaybuf)
|
|
{
|
|
m_displaybuf = new float[lines];
|
|
memset(m_displaybuf, 0, lines * sizeof(float));
|
|
}
|
|
|
|
int additions = 0;
|
|
for (int i = 1; i < lines; i++)
|
|
additions += i;
|
|
|
|
const int maxbin = Round32(15000.0f / samplerate * bins * 2.0f);
|
|
|
|
float increase = (float)(maxbin - lines - 1) / additions;
|
|
|
|
for (int i = 0; i < samples; i++)
|
|
{
|
|
m_fft.AddSample(m_buf[i]);
|
|
m_samplecounter++;
|
|
|
|
if (m_samplecounter % 32 == 0)
|
|
{
|
|
m_fft.ApplyWindow();
|
|
fftwf_execute(m_fft.m_plan);
|
|
|
|
m_nrffts++;
|
|
for (int j = 0; j < bins; j++)
|
|
m_fftbuf[j] += cabsf(m_fft.m_outbuf[j]) / m_fft.m_bufsize;
|
|
}
|
|
|
|
if (m_samplecounter % (samplerate / 30) == 0)
|
|
{
|
|
m_fft.ApplyWindow();
|
|
fftwf_execute(m_fft.m_plan);
|
|
|
|
string out;
|
|
float start = 0.0f;
|
|
float add = 1.0f;
|
|
for (int j = 0; j < lines; j++)
|
|
{
|
|
float next = start + add;
|
|
|
|
int bin = Round32(start) + 1;
|
|
int nrbins = Round32(next - start);
|
|
float outval = 0.0f;
|
|
for (int k = bin; k < bin + nrbins; k++)
|
|
outval += m_fftbuf[k] / m_nrffts;
|
|
|
|
m_displaybuf[j] = m_displaybuf[j] * decay + outval * (1.0f - decay);
|
|
out += string(Clamp(Round32((log10(m_displaybuf[j]) * 20.0f) + 30.0f) * 1.5f, 1, 48), '|');
|
|
out += '\n';
|
|
|
|
start = next;
|
|
add += increase;
|
|
}
|
|
|
|
//int64_t sleeptime = audiotime + Round64(1000000.0 / (double)samplerate * (double)i) - GetTimeUs();
|
|
//SendData(sleeptime);
|
|
SendData(audiotime + Round64(1000000.0 / (double)samplerate * (double)i));
|
|
//USleep(sleeptime);
|
|
static int64_t prev;
|
|
int64_t now = GetTimeUs();
|
|
int64_t interval = now - prev;
|
|
prev = now;
|
|
//printf("samples:%i\nsleeptime:%" PRIi64"\ninterval:%" PRIi64 "\nstart\n%send\n", samples, sleeptime, interval, out.c_str());
|
|
//fflush(stdout);
|
|
|
|
memset(m_fftbuf, 0, bins * sizeof(float));
|
|
m_nrffts = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CBitVis::Cleanup()
|
|
{
|
|
m_jackclient.Disconnect();
|
|
}
|
|
|
|
void CBitVis::SendData(int64_t time)
|
|
{
|
|
if (!m_socket.IsOpen())
|
|
return;
|
|
|
|
CTcpData data;
|
|
data.SetData(":00");
|
|
|
|
const int lines = 48;
|
|
const int columns = 120;
|
|
|
|
if (!m_peakholds)
|
|
{
|
|
m_peakholds = new peak[columns];
|
|
memset(m_peakholds, 0, columns * sizeof(peak));
|
|
}
|
|
|
|
for (int y = lines - 1; y >= 0; y--)
|
|
{
|
|
uint8_t line[columns / 4];
|
|
for (int x = 0; x < columns / 4; x++)
|
|
{
|
|
uint8_t pixel = 0;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
pixel <<= 2;
|
|
int value = Round32((log10(m_displaybuf[x * 4 + i]) * 20.0f) + 55.0f) * 1.0f;
|
|
if (value > y)
|
|
pixel |= 1;
|
|
|
|
peak& currpeak = m_peakholds[x * 4 + i];
|
|
if (value >= Round32(currpeak.value))
|
|
{
|
|
currpeak.value = value;
|
|
currpeak.time = time;
|
|
}
|
|
|
|
if (Round32(currpeak.value) == y)
|
|
pixel |= 2;
|
|
|
|
if (time - currpeak.time > 500000 && Round32(currpeak.value) > 0)
|
|
currpeak.value -= 0.01f;
|
|
}
|
|
line[x] = pixel;
|
|
}
|
|
data.SetData(line, sizeof(line), true);
|
|
}
|
|
|
|
uint8_t end[10];
|
|
memset(end, 0, sizeof(end));
|
|
data.SetData(end, sizeof(end), true);
|
|
|
|
USleep(time - GetTimeUs());
|
|
|
|
if (m_socket.Write(data) == FAIL)
|
|
{
|
|
LogError("%s", m_socket.GetError().c_str());
|
|
m_socket.Close();
|
|
}
|
|
}
|
|
|
|
void CBitVis::JackError(const char* jackerror)
|
|
{
|
|
LogDebug("%s", jackerror);
|
|
}
|
|
|
|
void CBitVis::JackInfo(const char* jackinfo)
|
|
{
|
|
LogDebug("%s", jackinfo);
|
|
}
|
|
|