/* * 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 . */ #include "util/inclstdint.h" #include "bitvis.h" #include "util/log.h" #include "util/misc.h" #include "util/timeutils.h" #include #include #include #include #include using namespace std; #define CONNECTINTERVAL 10000000 CBitVis::CBitVis(int argc, char *argv[]) { g_printdebuglevel = true; m_stop = false; m_buf = NULL; m_bufsize = 0; m_fftbuf = NULL; m_samplecounter = 0; m_nrffts = 0; } 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; while (!m_stop) { 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(); lastconnect = GetTimeUs(); } uint8_t msg; while ((msg = m_jackclient.GetMessage()) != MsgNone) LogDebug("got message %s from jack client", MsgToString(msg)); 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 = 46; int samplerate; int samples; if ((samples = m_jackclient.GetAudio(m_buf, m_bufsize, samplerate)) > 0) { m_fft.Allocate(bins * 2); if (!m_fftbuf) { m_fftbuf = new float[bins]; memset(m_fftbuf, 0, bins * 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) / 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 / 20) == 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); int nrbins = Round32(next - start); float outval = 0.0f; for (int k = bin; k < bin + nrbins; k++) outval += m_fftbuf[k] / m_nrffts; out += string(Clamp(Round32(outval * 300.0f), 1, 100), '|'); out += '\n'; start = next; add += increase; } printf("start\n%send\n", out.c_str()); fflush(stdout); memset(m_fftbuf, 0, bins * sizeof(float)); m_nrffts = 0; } } } } void CBitVis::Cleanup() { m_jackclient.Disconnect(); } void CBitVis::JackError(const char* jackerror) { LogDebug("%s", jackerror); } void CBitVis::JackInfo(const char* jackinfo) { LogDebug("%s", jackinfo); }