added: bitvis (WIP)
This commit is contained in:
parent
8eb8117da3
commit
4d4f7c585c
22 changed files with 2060 additions and 4 deletions
254
src/bitvis.cpp
Normal file
254
src/bitvis.cpp
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
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 >= samplerate / 30)
|
||||||
|
{
|
||||||
|
m_samplecounter = 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 += cabsf(m_fft.m_outbuf[k]) / m_fft.m_bufsize;
|
||||||
|
|
||||||
|
out += string(Clamp(Round32(outval * 100.0f), 1, 100), '|');
|
||||||
|
out += '\n';
|
||||||
|
|
||||||
|
start = next;
|
||||||
|
add += increase;
|
||||||
|
}
|
||||||
|
printf("start\n%send\n", out.c_str());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVis::Cleanup()
|
||||||
|
{
|
||||||
|
m_jackclient.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVis::JackError(const char* jackerror)
|
||||||
|
{
|
||||||
|
LogDebug("%s", jackerror);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBitVis::JackInfo(const char* jackinfo)
|
||||||
|
{
|
||||||
|
LogDebug("%s", jackinfo);
|
||||||
|
}
|
||||||
|
|
53
src/bitvis.h
Normal file
53
src/bitvis.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BITVIS_H
|
||||||
|
#define BITVIS_H
|
||||||
|
|
||||||
|
#include "jackclient.h"
|
||||||
|
#include "fft.h"
|
||||||
|
|
||||||
|
class CBitVis
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CBitVis(int argc, char *argv[]);
|
||||||
|
~CBitVis();
|
||||||
|
|
||||||
|
void Setup();
|
||||||
|
void Process();
|
||||||
|
void Cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_stop;
|
||||||
|
CJackClient m_jackclient;
|
||||||
|
int m_signalfd;
|
||||||
|
Cfft m_fft;
|
||||||
|
float* m_buf;
|
||||||
|
int m_bufsize;
|
||||||
|
float* m_fftbuf;
|
||||||
|
int m_samplecounter;
|
||||||
|
int m_nrffts;
|
||||||
|
|
||||||
|
void SetupSignals();
|
||||||
|
void ProcessSignalfd();
|
||||||
|
void ProcessAudio();
|
||||||
|
static void JackError(const char* jackerror);
|
||||||
|
static void JackInfo(const char* jackinfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //BITVIS_H
|
49
src/clientmessage.h
Normal file
49
src/clientmessage.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* bobdsp
|
||||||
|
* Copyright (C) Bob 2012
|
||||||
|
*
|
||||||
|
* bobdsp 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.
|
||||||
|
*
|
||||||
|
* bobdsp 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CLIENTMESSAGE_H
|
||||||
|
#define CLIENTMESSAGE_H
|
||||||
|
|
||||||
|
#include "util/inclstdint.h"
|
||||||
|
|
||||||
|
enum ClientMessage
|
||||||
|
{
|
||||||
|
MsgNone,
|
||||||
|
MsgExited,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const char* MsgToString(ClientMessage msg)
|
||||||
|
{
|
||||||
|
static const char* msgstrings[] =
|
||||||
|
{
|
||||||
|
"MsgNone",
|
||||||
|
"MsgExited",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (msg >= 0 && (size_t)msg < (sizeof(msgstrings) / sizeof(msgstrings[0])))
|
||||||
|
return msgstrings[msg];
|
||||||
|
else
|
||||||
|
return "ERROR: INVALID MESSAGE";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* MsgToString(uint8_t msg)
|
||||||
|
{
|
||||||
|
return MsgToString((ClientMessage)msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //CLIENTMESSAGE_H
|
1
src/config.h
Normal file
1
src/config.h
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include "../build/config.h"
|
100
src/fft.cpp
Normal file
100
src/fft.cpp
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* 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 <math.h>
|
||||||
|
#include <stdlib.h> //TODO: REMOVE
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "fft.h"
|
||||||
|
#include "util/timeutils.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
Cfft::Cfft()
|
||||||
|
{
|
||||||
|
m_inbuf = NULL;
|
||||||
|
m_fftin = NULL;
|
||||||
|
m_outbuf = NULL;
|
||||||
|
m_window = NULL;
|
||||||
|
m_bufsize = 0;
|
||||||
|
m_plan = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cfft::~Cfft()
|
||||||
|
{
|
||||||
|
Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cfft::Allocate(unsigned int size)
|
||||||
|
{
|
||||||
|
if (size != m_bufsize)
|
||||||
|
{
|
||||||
|
Free();
|
||||||
|
|
||||||
|
m_bufsize = size;
|
||||||
|
m_inbuf = new float[m_bufsize];
|
||||||
|
m_fftin = (float*)fftw_malloc(m_bufsize * sizeof(float));
|
||||||
|
m_outbuf = (fftwf_complex*)fftw_malloc(m_bufsize * sizeof(fftwf_complex));
|
||||||
|
m_window = new float[m_bufsize];
|
||||||
|
|
||||||
|
//create a hamming window
|
||||||
|
for (unsigned int i = 0; i < m_bufsize; i++)
|
||||||
|
m_window[i] = 0.54f - 0.46f * cosf(2.0f * M_PI * i / (m_bufsize - 1.0f));
|
||||||
|
|
||||||
|
Log("Building fft plan");
|
||||||
|
int64_t start = GetTimeUs();
|
||||||
|
m_plan = fftwf_plan_dft_r2c_1d(m_bufsize, m_fftin, m_outbuf, FFTW_MEASURE);
|
||||||
|
Log("Build fft plan in %.0f ms", (double)(GetTimeUs() - start) / 1000.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cfft::Free()
|
||||||
|
{
|
||||||
|
delete[] m_inbuf;
|
||||||
|
fftw_free(m_fftin);
|
||||||
|
fftw_free(m_outbuf);
|
||||||
|
delete[] m_window;
|
||||||
|
m_inbuf = NULL;
|
||||||
|
m_fftin = NULL;
|
||||||
|
m_outbuf = NULL;
|
||||||
|
m_window = NULL;
|
||||||
|
m_bufsize = 0;
|
||||||
|
|
||||||
|
if (m_plan)
|
||||||
|
{
|
||||||
|
fftwf_destroy_plan(m_plan);
|
||||||
|
m_plan = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cfft::ApplyWindow()
|
||||||
|
{
|
||||||
|
float* in = m_inbuf;
|
||||||
|
float* inend = m_inbuf + m_bufsize;
|
||||||
|
float* window = m_window;
|
||||||
|
float* out = m_fftin;
|
||||||
|
|
||||||
|
while (in != inend)
|
||||||
|
*(out++) = *(in++);// * *(window++);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cfft::AddSample(float sample)
|
||||||
|
{
|
||||||
|
memmove(m_inbuf, m_inbuf + 1, (m_bufsize - 1) * sizeof(float));
|
||||||
|
m_inbuf[m_bufsize - 1] = sample;
|
||||||
|
}
|
||||||
|
|
46
src/fft.h
Normal file
46
src/fft.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FFT_H
|
||||||
|
#define FFT_H
|
||||||
|
|
||||||
|
#include <complex.h>
|
||||||
|
#include <fftw3.h>
|
||||||
|
|
||||||
|
class Cfft
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Cfft();
|
||||||
|
~Cfft();
|
||||||
|
|
||||||
|
void Allocate(unsigned int size);
|
||||||
|
void Free();
|
||||||
|
void ApplyWindow();
|
||||||
|
void AddSample(float sample);
|
||||||
|
|
||||||
|
float* m_inbuf;
|
||||||
|
float* m_fftin;
|
||||||
|
float* m_window;
|
||||||
|
fftwf_complex* m_outbuf;
|
||||||
|
unsigned int m_bufsize;
|
||||||
|
fftwf_plan m_plan;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
#endif //FFT_H
|
315
src/jackclient.cpp
Normal file
315
src/jackclient.cpp
Normal file
|
@ -0,0 +1,315 @@
|
||||||
|
/*
|
||||||
|
* bobdsp
|
||||||
|
* Copyright (C) Bob 2012
|
||||||
|
*
|
||||||
|
* bobdsp 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.
|
||||||
|
*
|
||||||
|
* bobdsp 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE //for pipe2
|
||||||
|
#endif //_GNU_SOURCE
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <complex.h>
|
||||||
|
#include <fftw3.h>
|
||||||
|
|
||||||
|
#include "util/inclstdint.h"
|
||||||
|
#include "util/misc.h"
|
||||||
|
#include "util/timeutils.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "util/lock.h"
|
||||||
|
|
||||||
|
#include "jackclient.h"
|
||||||
|
#include "fft.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
CJackClient::CJackClient()
|
||||||
|
{
|
||||||
|
m_name = "bitvis";
|
||||||
|
m_client = NULL;
|
||||||
|
m_jackport = NULL;
|
||||||
|
m_connected = false;
|
||||||
|
m_wasconnected = true;
|
||||||
|
m_exitstatus = (jack_status_t)0;
|
||||||
|
m_samplerate = 0;
|
||||||
|
m_outsamplerate = 40000;
|
||||||
|
m_buf = NULL;
|
||||||
|
m_bufsize = 0;
|
||||||
|
m_bufupdated = false;
|
||||||
|
m_srcstate = NULL;
|
||||||
|
m_outsamples = 0;
|
||||||
|
|
||||||
|
if (pipe2(m_pipe, O_NONBLOCK) == -1)
|
||||||
|
{
|
||||||
|
LogError("creating msg pipe for client \"%s\": %s", m_name.c_str(), GetErrno().c_str());
|
||||||
|
m_pipe[0] = m_pipe[1] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CJackClient::~CJackClient()
|
||||||
|
{
|
||||||
|
Disconnect();
|
||||||
|
|
||||||
|
if (m_pipe[0] != -1)
|
||||||
|
close(m_pipe[0]);
|
||||||
|
if (m_pipe[1] != -1)
|
||||||
|
close(m_pipe[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CJackClient::Connect()
|
||||||
|
{
|
||||||
|
m_connected = ConnectInternal();
|
||||||
|
if (!m_connected)
|
||||||
|
Disconnect();
|
||||||
|
else
|
||||||
|
m_wasconnected = true;
|
||||||
|
|
||||||
|
return m_connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CJackClient::ConnectInternal()
|
||||||
|
{
|
||||||
|
if (m_connected)
|
||||||
|
return true; //already connected
|
||||||
|
|
||||||
|
LogDebug("Connecting client \"%s\" to jackd", m_name.c_str());
|
||||||
|
|
||||||
|
//this is set in PJackInfoShutdownCallback(), init to 0 here so we know when the jack thread has exited
|
||||||
|
m_exitstatus = (jack_status_t)0;
|
||||||
|
m_exitreason.clear();
|
||||||
|
|
||||||
|
//try to connect to jackd
|
||||||
|
m_client = jack_client_open(m_name.substr(0, jack_client_name_size() - 1).c_str(), JackNoStartServer, NULL);
|
||||||
|
if (m_client == NULL)
|
||||||
|
{
|
||||||
|
if (m_wasconnected || g_printdebuglevel)
|
||||||
|
{
|
||||||
|
LogError("Client \"%s\" error connecting to jackd: \"%s\"", m_name.c_str(), GetErrno().c_str());
|
||||||
|
m_wasconnected = false; //only print this to the log once
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//we want to know when the jack thread shuts down, so we can restart it
|
||||||
|
jack_on_info_shutdown(m_client, SJackInfoShutdownCallback, this);
|
||||||
|
|
||||||
|
m_samplerate = jack_get_sample_rate(m_client);
|
||||||
|
|
||||||
|
Log("Client \"%s\" connected to jackd, got name \"%s\", samplerate %" PRIi32,
|
||||||
|
m_name.c_str(), jack_get_client_name(m_client), m_samplerate);
|
||||||
|
|
||||||
|
int returnv;
|
||||||
|
|
||||||
|
//SJackProcessCallback gets called when jack has new audio data to process
|
||||||
|
returnv = jack_set_process_callback(m_client, SJackProcessCallback, this);
|
||||||
|
if (returnv != 0)
|
||||||
|
{
|
||||||
|
LogError("Client \"%s\" error %i setting process callback: \"%s\"",
|
||||||
|
m_name.c_str(), returnv, GetErrno().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_jackport = jack_port_register(m_client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
||||||
|
if (m_jackport == NULL)
|
||||||
|
{
|
||||||
|
Log("Error registering jack port: %s", GetErrno().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int error;
|
||||||
|
m_srcstate = src_new(SRC_SINC_FASTEST, 1, &error);
|
||||||
|
|
||||||
|
//everything set up, activate
|
||||||
|
returnv = jack_activate(m_client);
|
||||||
|
if (returnv != 0)
|
||||||
|
{
|
||||||
|
LogError("Client \"%s\" error %i activating client: \"%s\"",
|
||||||
|
m_name.c_str(), returnv, GetErrno().c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJackClient::Disconnect()
|
||||||
|
{
|
||||||
|
if (m_client)
|
||||||
|
{
|
||||||
|
//deactivate the client before everything else
|
||||||
|
int returnv = jack_deactivate(m_client);
|
||||||
|
if (returnv != 0)
|
||||||
|
LogError("Client \"%s\" error %i deactivating client: \"%s\"",
|
||||||
|
m_name.c_str(), returnv, GetErrno().c_str());
|
||||||
|
|
||||||
|
//close the jack client
|
||||||
|
returnv = jack_client_close(m_client);
|
||||||
|
if (returnv != 0)
|
||||||
|
LogError("Client \"%s\" error %i closing client: \"%s\"",
|
||||||
|
m_name.c_str(), returnv, GetErrno().c_str());
|
||||||
|
|
||||||
|
m_client = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connected = false;
|
||||||
|
m_exitstatus = (jack_status_t)0;
|
||||||
|
m_samplerate = 0;
|
||||||
|
|
||||||
|
delete[] m_buf;
|
||||||
|
m_buf = NULL;
|
||||||
|
m_bufsize = 0;
|
||||||
|
m_bufupdated = false;
|
||||||
|
|
||||||
|
if (m_srcstate)
|
||||||
|
{
|
||||||
|
src_delete(m_srcstate);
|
||||||
|
m_srcstate = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns true when the message has been sent or pipe is broken
|
||||||
|
//returns false when the message write needs to be retried
|
||||||
|
bool CJackClient::WriteMessage(uint8_t msg)
|
||||||
|
{
|
||||||
|
if (m_pipe[1] == -1)
|
||||||
|
return true; //can't write
|
||||||
|
|
||||||
|
int returnv = write(m_pipe[1], &msg, 1);
|
||||||
|
if (returnv == 1)
|
||||||
|
return true; //write successful
|
||||||
|
|
||||||
|
if (returnv == -1 && errno != EAGAIN)
|
||||||
|
{
|
||||||
|
LogError("Client \"%s\" error writing msg %s to pipe: \"%s\"", m_name.c_str(), MsgToString(msg), GetErrno().c_str());
|
||||||
|
if (errno != EINTR)
|
||||||
|
{
|
||||||
|
close(m_pipe[1]);
|
||||||
|
m_pipe[1] = -1;
|
||||||
|
return true; //pipe broken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; //need to try again
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMessage CJackClient::GetMessage()
|
||||||
|
{
|
||||||
|
if (m_pipe[0] == -1)
|
||||||
|
return MsgNone;
|
||||||
|
|
||||||
|
uint8_t msg;
|
||||||
|
int returnv = read(m_pipe[0], &msg, 1);
|
||||||
|
if (returnv == 1)
|
||||||
|
{
|
||||||
|
return (ClientMessage)msg;
|
||||||
|
}
|
||||||
|
else if (returnv == -1 && errno != EAGAIN)
|
||||||
|
{
|
||||||
|
LogError("Client \"%s\" error reading msg from pipe: \"%s\"", m_name.c_str(), GetErrno().c_str());
|
||||||
|
if (errno != EINTR)
|
||||||
|
{
|
||||||
|
close(m_pipe[0]);
|
||||||
|
m_pipe[0] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MsgNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CJackClient::SJackProcessCallback(jack_nframes_t nframes, void *arg)
|
||||||
|
{
|
||||||
|
((CJackClient*)arg)->PJackProcessCallback(nframes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJackClient::PJackProcessCallback(jack_nframes_t nframes)
|
||||||
|
{
|
||||||
|
if (m_bufsize < nframes)
|
||||||
|
{
|
||||||
|
delete[] m_buf;
|
||||||
|
m_buf = new float[nframes];
|
||||||
|
m_bufsize = nframes;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLock lock(m_condition);
|
||||||
|
|
||||||
|
float* jackptr = (float*)jack_port_get_buffer(m_jackport, nframes);
|
||||||
|
|
||||||
|
SRC_DATA srcdata = {};
|
||||||
|
srcdata.data_in = jackptr;
|
||||||
|
srcdata.data_out = m_buf;
|
||||||
|
srcdata.input_frames = nframes;
|
||||||
|
srcdata.output_frames = nframes;
|
||||||
|
srcdata.src_ratio = (double)m_outsamplerate / m_samplerate;
|
||||||
|
|
||||||
|
src_process(m_srcstate, &srcdata);
|
||||||
|
m_outsamples = srcdata.output_frames_gen;
|
||||||
|
|
||||||
|
m_bufupdated = true;
|
||||||
|
lock.Leave();
|
||||||
|
m_condition.Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CJackClient::GetAudio(float*& buf, int& bufsize, int& samplerate)
|
||||||
|
{
|
||||||
|
CLock lock(m_condition);
|
||||||
|
m_condition.Wait(1000000, m_bufupdated, false);
|
||||||
|
|
||||||
|
if (!m_bufupdated)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
samplerate = m_outsamplerate;
|
||||||
|
|
||||||
|
if (bufsize < m_outsamples)
|
||||||
|
{
|
||||||
|
bufsize = m_outsamples;
|
||||||
|
buf = (float*)realloc(buf, bufsize * sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buf, m_buf, m_outsamples * sizeof(float));
|
||||||
|
|
||||||
|
m_bufupdated = false;
|
||||||
|
|
||||||
|
return m_outsamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJackClient::SJackInfoShutdownCallback(jack_status_t code, const char *reason, void *arg)
|
||||||
|
{
|
||||||
|
((CJackClient*)arg)->PJackInfoShutdownCallback(code, reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CJackClient::PJackInfoShutdownCallback(jack_status_t code, const char *reason)
|
||||||
|
{
|
||||||
|
//save the exit code, this will be read from the loop in main()
|
||||||
|
//make sure reason is saved before code, to make it thread safe
|
||||||
|
//since main() will read m_exitstatus first, then m_exitreason if necessary
|
||||||
|
m_exitreason = reason;
|
||||||
|
m_exitstatus = code;
|
||||||
|
|
||||||
|
//send message to the main loop
|
||||||
|
//try for one second to make sure it gets there
|
||||||
|
int64_t start = GetTimeUs();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (WriteMessage(MsgExited))
|
||||||
|
return;
|
||||||
|
|
||||||
|
USleep(100); //don't busy spin
|
||||||
|
}
|
||||||
|
while (GetTimeUs() - start < 1000000);
|
||||||
|
|
||||||
|
LogError("Client \"%s\" unable to write exit msg to pipe", m_name.c_str());
|
||||||
|
}
|
||||||
|
|
79
src/jackclient.h
Normal file
79
src/jackclient.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* bobdsp
|
||||||
|
* Copyright (C) Bob 2012
|
||||||
|
*
|
||||||
|
* bobdsp 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.
|
||||||
|
*
|
||||||
|
* bobdsp 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JACKCLIENT_H
|
||||||
|
#define JACKCLIENT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <jack/jack.h>
|
||||||
|
#include <samplerate.h>
|
||||||
|
|
||||||
|
#include "fft.h"
|
||||||
|
#include "clientmessage.h"
|
||||||
|
#include "util/condition.h"
|
||||||
|
|
||||||
|
class CJackClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CJackClient();
|
||||||
|
~CJackClient();
|
||||||
|
|
||||||
|
bool Connect();
|
||||||
|
void Disconnect();
|
||||||
|
bool IsConnected() { return m_connected; }
|
||||||
|
int MsgPipe() { return m_pipe[0]; }
|
||||||
|
ClientMessage GetMessage();
|
||||||
|
|
||||||
|
jack_status_t ExitStatus() { return m_exitstatus; }
|
||||||
|
const std::string& ExitReason() { return m_exitreason; }
|
||||||
|
|
||||||
|
int Samplerate() { return m_samplerate; }
|
||||||
|
int GetAudio(float*& buf, int& bufsize, int& samplerate);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_connected;
|
||||||
|
bool m_wasconnected;
|
||||||
|
jack_client_t* m_client;
|
||||||
|
jack_port_t* m_jackport;
|
||||||
|
std::string m_name;
|
||||||
|
int m_samplerate;
|
||||||
|
int m_outsamplerate;
|
||||||
|
jack_status_t m_exitstatus;
|
||||||
|
std::string m_exitreason;
|
||||||
|
int m_portevents;
|
||||||
|
int m_pipe[2];
|
||||||
|
CCondition m_condition;
|
||||||
|
float* m_buf;
|
||||||
|
unsigned int m_bufsize;
|
||||||
|
bool m_bufupdated;
|
||||||
|
SRC_STATE* m_srcstate;
|
||||||
|
int m_outsamples;
|
||||||
|
|
||||||
|
bool ConnectInternal();
|
||||||
|
void CheckMessages();
|
||||||
|
bool WriteMessage(uint8_t message);
|
||||||
|
|
||||||
|
static int SJackProcessCallback(jack_nframes_t nframes, void *arg);
|
||||||
|
void PJackProcessCallback(jack_nframes_t nframes);
|
||||||
|
|
||||||
|
static void SJackInfoShutdownCallback(jack_status_t code, const char *reason, void *arg);
|
||||||
|
void PJackInfoShutdownCallback(jack_status_t code, const char *reason);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //JACKCLIENT_H
|
|
@ -16,7 +16,16 @@
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "bitvis.h"
|
||||||
|
|
||||||
int main (int argc, char *argv[])
|
int main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
CBitVis bitvis(argc, argv);
|
||||||
|
|
||||||
|
bitvis.Setup();
|
||||||
|
bitvis.Process();
|
||||||
|
bitvis.Cleanup();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
99
src/util/condition.cpp
Normal file
99
src/util/condition.cpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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 <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "condition.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
CCondition::CCondition()
|
||||||
|
{
|
||||||
|
pthread_cond_init(&m_cond, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CCondition::~CCondition()
|
||||||
|
{
|
||||||
|
pthread_cond_destroy(&m_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondition::Signal()
|
||||||
|
{
|
||||||
|
pthread_cond_signal(&m_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondition::Broadcast()
|
||||||
|
{
|
||||||
|
pthread_cond_broadcast(&m_cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
//should have locked before getting here
|
||||||
|
bool CCondition::Wait(int64_t usecs /*= -1*/)
|
||||||
|
{
|
||||||
|
assert(m_refcount > 0); //if refcount is 0, then the mutex is not locked
|
||||||
|
|
||||||
|
//our mutex is created with PTHREAD_MUTEX_RECURSIVE, which doesn't work with condition variables
|
||||||
|
//if the mutex is locked multiple times
|
||||||
|
//we keep a refcount, then we unlock enough times so that the mutex is locked only once
|
||||||
|
int refcount = m_refcount;
|
||||||
|
while (m_refcount > 1)
|
||||||
|
Unlock();
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
if (usecs < 0)
|
||||||
|
{
|
||||||
|
//pthread_cond_wait will unlock and lock the mutex
|
||||||
|
m_refcount--;
|
||||||
|
pthread_cond_wait(&m_cond, &m_mutex);
|
||||||
|
m_refcount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//get the current time
|
||||||
|
struct timespec currtime;
|
||||||
|
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
|
||||||
|
clock_gettime(CLOCK_REALTIME, &currtime);
|
||||||
|
#else
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
currtime.tv_sec = tv.tv_sec;
|
||||||
|
currtime.tv_nsec = tv.tv_usec * 1000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//add the number of microseconds
|
||||||
|
|
||||||
|
currtime.tv_sec += usecs / 1000000;
|
||||||
|
currtime.tv_nsec += (usecs % 1000000) * 1000;
|
||||||
|
|
||||||
|
if (currtime.tv_nsec >= 1000000000)
|
||||||
|
{
|
||||||
|
currtime.tv_sec += currtime.tv_nsec / 1000000000;
|
||||||
|
currtime.tv_nsec %= 1000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
//pthread_cond_timedwait will unlock and lock the mutex
|
||||||
|
m_refcount--;
|
||||||
|
result = pthread_cond_timedwait(&m_cond, &m_mutex, &currtime);
|
||||||
|
m_refcount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (m_refcount < refcount)
|
||||||
|
Lock();
|
||||||
|
|
||||||
|
return result == 0;
|
||||||
|
}
|
69
src/util/condition.h
Normal file
69
src/util/condition.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CCONDITION
|
||||||
|
#define CCONDITION
|
||||||
|
|
||||||
|
#include "inclstdint.h"
|
||||||
|
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "timeutils.h"
|
||||||
|
|
||||||
|
//pthread condition variable class
|
||||||
|
class CCondition : public CMutex
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CCondition();
|
||||||
|
~CCondition();
|
||||||
|
|
||||||
|
void Signal();
|
||||||
|
void Broadcast();
|
||||||
|
bool Wait(int64_t usecs = -1);
|
||||||
|
|
||||||
|
template <class TPredicate>
|
||||||
|
bool Wait(int64_t usecs, TPredicate& predicate, TPredicate compval)
|
||||||
|
{
|
||||||
|
if (usecs < 0)
|
||||||
|
{
|
||||||
|
while (predicate == compval)
|
||||||
|
Wait();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int64_t now = GetTimeUs();
|
||||||
|
int64_t end = now + usecs;
|
||||||
|
while (now <= end && predicate == compval)
|
||||||
|
{
|
||||||
|
Wait(end - now);
|
||||||
|
if (predicate != compval)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
now = GetTimeUs();
|
||||||
|
}
|
||||||
|
|
||||||
|
return predicate != compval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
pthread_cond_t m_cond;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CCONDITION
|
39
src/util/inclstdint.h
Normal file
39
src/util/inclstdint.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//file to include stdint.h, __STDC_CONSTANT_MACROS and __STDC_LIMIT_MACROS
|
||||||
|
//need to be defined before it
|
||||||
|
|
||||||
|
#ifndef INCLSTDINT_H
|
||||||
|
#define INCLSTDINT_H
|
||||||
|
|
||||||
|
#ifndef __STDC_CONSTANT_MACROS
|
||||||
|
#define __STDC_CONSTANT_MACROS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __STDC_LIMIT_MACROS
|
||||||
|
#define __STDC_LIMIT_MACROS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __STDC_FORMAT_MACROS
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#endif
|
62
src/util/lock.h
Normal file
62
src/util/lock.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CLOCK
|
||||||
|
#define CLOCK
|
||||||
|
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "condition.h"
|
||||||
|
|
||||||
|
class CLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CLock(CMutex& mutex) : m_mutex(mutex)
|
||||||
|
{
|
||||||
|
m_haslock = false;
|
||||||
|
Enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
~CLock()
|
||||||
|
{
|
||||||
|
Leave();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enter()
|
||||||
|
{
|
||||||
|
if (!m_haslock)
|
||||||
|
{
|
||||||
|
m_mutex.Lock();
|
||||||
|
m_haslock = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Leave()
|
||||||
|
{
|
||||||
|
if (m_haslock)
|
||||||
|
{
|
||||||
|
m_mutex.Unlock();
|
||||||
|
m_haslock = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CMutex& m_mutex;
|
||||||
|
bool m_haslock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CLOCK
|
216
src/util/log.cpp
Normal file
216
src/util/log.cpp
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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 <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "lock.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
bool g_logtostderr = true;
|
||||||
|
bool g_printlogtofile = true;
|
||||||
|
bool g_printdebuglevel = false;
|
||||||
|
string g_logfilename;
|
||||||
|
|
||||||
|
//as pointers, so we can allocate these after the locked memory is set up
|
||||||
|
static CMutex* g_logmutex;
|
||||||
|
static ofstream* g_logfile;
|
||||||
|
|
||||||
|
static int g_logbuffsize; //size of the buffer
|
||||||
|
static char* g_logbuff; //buffer for vsnprintf
|
||||||
|
static vector<string>* g_logstrings; //we save any log lines here while the log isn't open
|
||||||
|
|
||||||
|
//returns hour:minutes:seconds:microseconds
|
||||||
|
string GetStrTime()
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
struct tm time;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
//get current time
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
now = tv.tv_sec; //seconds since EPOCH
|
||||||
|
localtime_r(&now, &time); //convert to hours, minutes, seconds
|
||||||
|
|
||||||
|
char buff[16];
|
||||||
|
snprintf(buff, sizeof(buff), "%02i:%02i:%02i.%06i", time.tm_hour, time.tm_min, time.tm_sec, (int)tv.tv_usec);
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InitLog(string filename, ofstream& logfile)
|
||||||
|
{
|
||||||
|
string homepath;
|
||||||
|
if (!GetHomePath(homepath))
|
||||||
|
{
|
||||||
|
PrintError("Unable to get home directory path");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string directory = homepath + ".bitvis/";
|
||||||
|
string fullpath = directory + filename;
|
||||||
|
|
||||||
|
//try to make the directory the log goes in
|
||||||
|
if (mkdir(directory.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
|
||||||
|
{
|
||||||
|
//if it already exists we're ok
|
||||||
|
if (errno != EEXIST)
|
||||||
|
{
|
||||||
|
PrintError("unable to make directory " + directory + ":\n" + GetErrno());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//we keep around 5 old logfiles
|
||||||
|
for (int i = 4; i > 0; i--)
|
||||||
|
rename(string(fullpath + ".old." + ToString(i)).c_str(), string(fullpath + ".old." + ToString(i + 1)).c_str());
|
||||||
|
|
||||||
|
rename(fullpath.c_str(), string(fullpath + ".old.1").c_str());
|
||||||
|
|
||||||
|
//open the logfile in append mode
|
||||||
|
logfile.open(fullpath.c_str());
|
||||||
|
if (logfile.fail())
|
||||||
|
{
|
||||||
|
PrintError("unable to open " + fullpath + ":\n" + GetErrno());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("successfully set up logfile %s", fullpath.c_str());
|
||||||
|
|
||||||
|
g_logfilename = fullpath;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//we only want the function name and the namespace, so we search for '(' and get the string before that
|
||||||
|
string PruneFunction(string function)
|
||||||
|
{
|
||||||
|
size_t parenpos = function.find('(');
|
||||||
|
size_t spacepos = function.rfind(' ', parenpos);
|
||||||
|
|
||||||
|
if (parenpos == string::npos)
|
||||||
|
return function;
|
||||||
|
else if (spacepos == string::npos) //when called from a constructor, there's no return value, thus no space
|
||||||
|
return function.substr(0, parenpos);
|
||||||
|
else
|
||||||
|
return function.substr(spacepos + 1, parenpos - spacepos - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLogFile(std::string filename)
|
||||||
|
{
|
||||||
|
if (!g_logmutex)
|
||||||
|
g_logmutex = new CMutex;
|
||||||
|
|
||||||
|
CLock lock(*g_logmutex);
|
||||||
|
|
||||||
|
if (!g_logfile)
|
||||||
|
g_logfile = new ofstream;
|
||||||
|
|
||||||
|
//deallocate, so it will be allocated in PrintLog gain, but this time in locked memory
|
||||||
|
free(g_logbuff);
|
||||||
|
g_logbuff = NULL;
|
||||||
|
g_logbuffsize = 0;
|
||||||
|
|
||||||
|
if (!InitLog(filename, *g_logfile))
|
||||||
|
g_printlogtofile = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintLog (const char* fmt, const char* function, LogLevel loglevel, ...)
|
||||||
|
{
|
||||||
|
if (loglevel == LogLevelDebug && !g_printdebuglevel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (g_logmutex)
|
||||||
|
g_logmutex->Lock();
|
||||||
|
|
||||||
|
string logstr;
|
||||||
|
string funcstr;
|
||||||
|
va_list args;
|
||||||
|
int nrspaces;
|
||||||
|
|
||||||
|
va_start(args, loglevel);
|
||||||
|
|
||||||
|
//print to the logbuffer and check if our buffer is large enough
|
||||||
|
int neededbuffsize = vsnprintf(g_logbuff, g_logbuffsize, fmt, args);
|
||||||
|
if (neededbuffsize + 1 > g_logbuffsize)
|
||||||
|
{
|
||||||
|
g_logbuffsize = neededbuffsize + 1;
|
||||||
|
g_logbuff = (char*)(realloc(g_logbuff, g_logbuffsize)); //resize the buffer to the needed size
|
||||||
|
|
||||||
|
va_end(args); //need to reinit or we will crash
|
||||||
|
va_start(args, loglevel);
|
||||||
|
vsnprintf(g_logbuff, g_logbuffsize, fmt, args); //write to the buffer again
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (loglevel == LogLevelError)
|
||||||
|
logstr += "ERROR: ";
|
||||||
|
else if (loglevel == LogLevelDebug)
|
||||||
|
logstr += "DEBUG: ";
|
||||||
|
|
||||||
|
if (g_logbuff)
|
||||||
|
logstr += g_logbuff;
|
||||||
|
|
||||||
|
funcstr = "(" + PruneFunction(function) + ")";
|
||||||
|
nrspaces = 34 - funcstr.length();
|
||||||
|
if (nrspaces > 0)
|
||||||
|
funcstr.insert(funcstr.length(), nrspaces, ' ');
|
||||||
|
|
||||||
|
if (g_logfile && g_logfile->is_open() && g_printlogtofile)
|
||||||
|
{
|
||||||
|
//print any saved log lines
|
||||||
|
if (g_logstrings)
|
||||||
|
{
|
||||||
|
for (vector<string>::iterator it = g_logstrings->begin(); it != g_logstrings->end(); it++)
|
||||||
|
*g_logfile << *it << flush;
|
||||||
|
|
||||||
|
delete g_logstrings;
|
||||||
|
g_logstrings = NULL;
|
||||||
|
}
|
||||||
|
//write the string to the logfile
|
||||||
|
*g_logfile << GetStrTime() << " " << funcstr << " " << logstr << '\n' << flush;
|
||||||
|
}
|
||||||
|
else if (g_printlogtofile)
|
||||||
|
{
|
||||||
|
//save the log line if the log isn't open yet
|
||||||
|
if (!g_logstrings)
|
||||||
|
g_logstrings = new vector<string>;
|
||||||
|
g_logstrings->push_back(GetStrTime() + " " + funcstr + " " + logstr + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
//print to stdout when requested
|
||||||
|
if (g_logtostderr)
|
||||||
|
cerr << funcstr << logstr << '\n' << flush;
|
||||||
|
|
||||||
|
if (g_logmutex)
|
||||||
|
g_logmutex->Unlock();
|
||||||
|
}
|
44
src/util/log.h
Normal file
44
src/util/log.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* bobdsp
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* bobdsp 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.
|
||||||
|
*
|
||||||
|
* bobdsp 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LOG
|
||||||
|
#define LOG
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
enum LogLevel
|
||||||
|
{
|
||||||
|
LogLevelBasic,
|
||||||
|
LogLevelError,
|
||||||
|
LogLevelDebug,
|
||||||
|
};
|
||||||
|
|
||||||
|
//this has to be a macro, because we want __PRETTY_FUNCTION__
|
||||||
|
#define Log(fmt, ...) PrintLog(fmt, __PRETTY_FUNCTION__, LogLevelBasic, ##__VA_ARGS__)
|
||||||
|
#define LogError(fmt, ...) PrintLog(fmt, __PRETTY_FUNCTION__, LogLevelError, ##__VA_ARGS__)
|
||||||
|
#define LogDebug(fmt, ...) g_printdebuglevel ? PrintLog(fmt, __PRETTY_FUNCTION__, LogLevelDebug, ##__VA_ARGS__) : (void)0
|
||||||
|
|
||||||
|
void PrintLog (const char* fmt, const char* function, LogLevel loglevel, ...) __attribute__ ((format (printf, 1, 4)));
|
||||||
|
void SetLogFile(std::string logfile);
|
||||||
|
|
||||||
|
extern bool g_logtostderr;
|
||||||
|
extern bool g_printlogtofile;
|
||||||
|
extern bool g_printdebuglevel;
|
||||||
|
extern std::string g_logfilename;
|
||||||
|
|
||||||
|
#endif //LOG
|
209
src/util/misc.cpp
Normal file
209
src/util/misc.cpp
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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 <iostream>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace UTILNAMESPACE
|
||||||
|
{
|
||||||
|
void PrintError(const std::string& error)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: " << error << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the first word (separated by whitespace) from string data and place that in word
|
||||||
|
//then remove that word from string data
|
||||||
|
bool GetWord(string& data, string& word)
|
||||||
|
{
|
||||||
|
stringstream datastream(data);
|
||||||
|
string end;
|
||||||
|
|
||||||
|
datastream >> word;
|
||||||
|
if (datastream.fail())
|
||||||
|
{
|
||||||
|
data.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = data.find(word) + word.length();
|
||||||
|
|
||||||
|
if (pos >= data.length())
|
||||||
|
{
|
||||||
|
data.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = data.substr(pos);
|
||||||
|
|
||||||
|
datastream.clear();
|
||||||
|
datastream.str(data);
|
||||||
|
|
||||||
|
datastream >> end;
|
||||||
|
if (datastream.fail())
|
||||||
|
data.clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert . or , to the current locale for correct conversion of ascii float
|
||||||
|
void ConvertFloatLocale(std::string& strfloat)
|
||||||
|
{
|
||||||
|
static struct lconv* locale = localeconv();
|
||||||
|
|
||||||
|
size_t pos = strfloat.find_first_of(",.");
|
||||||
|
|
||||||
|
while (pos != string::npos)
|
||||||
|
{
|
||||||
|
strfloat.replace(pos, 1, 1, *locale->decimal_point);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (pos >= strfloat.size())
|
||||||
|
break;
|
||||||
|
|
||||||
|
pos = strfloat.find_first_of(",.", pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetHomePath(std::string& homepath)
|
||||||
|
{
|
||||||
|
const char* homeptr = getenv("HOME");
|
||||||
|
if (homeptr && strlen(homeptr) > 0)
|
||||||
|
{
|
||||||
|
homepath = PutSlashAtEnd(homeptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PutSlashAtEnd(const std::string& path)
|
||||||
|
{
|
||||||
|
if (path.empty())
|
||||||
|
return "/";
|
||||||
|
else if (path[path.length() - 1] != '/')
|
||||||
|
return path + '/';
|
||||||
|
else
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RemoveSlashAtEnd(const std::string& path)
|
||||||
|
{
|
||||||
|
if (path.length() > 0 && path[path.length() - 1] == '/')
|
||||||
|
return path.substr(0, path.length() - 1);
|
||||||
|
else
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PutSlashAtStart(const std::string& path)
|
||||||
|
{
|
||||||
|
if (path.empty())
|
||||||
|
return "/";
|
||||||
|
else if (path[0] != '/')
|
||||||
|
return string("/") + path;
|
||||||
|
else
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FileNameExtension(const std::string& path)
|
||||||
|
{
|
||||||
|
size_t pos = path.rfind('.');
|
||||||
|
if (pos == string::npos || pos >= path.length() - 1)
|
||||||
|
return "";
|
||||||
|
else
|
||||||
|
return path.substr(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToLower(const std::string& in)
|
||||||
|
{
|
||||||
|
string out;
|
||||||
|
for (string::const_iterator it = in.begin(); it != in.end(); it++)
|
||||||
|
out += (char)tolower(*it);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StrToBool(const std::string& data, bool& value)
|
||||||
|
{
|
||||||
|
std::string data2 = data;
|
||||||
|
std::string word;
|
||||||
|
if (!GetWord(data2, word))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (std::string::iterator it = word.begin(); it != word.end(); it++)
|
||||||
|
*it = tolower(*it);
|
||||||
|
|
||||||
|
if (word == "1" || word == "true" || word == "on" || word == "yes")
|
||||||
|
{
|
||||||
|
value = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (word == "0" || word == "false" || word == "off" || word == "no")
|
||||||
|
{
|
||||||
|
value = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int ivalue;
|
||||||
|
if (StrToInt(word, ivalue))
|
||||||
|
{
|
||||||
|
value = ivalue != 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//returns < 0 if a directory outside the root is accessed through ..
|
||||||
|
int DirLevel(const std::string& url)
|
||||||
|
{
|
||||||
|
size_t start = 0;
|
||||||
|
size_t pos;
|
||||||
|
|
||||||
|
int level = 0;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
pos = url.find('/', start);
|
||||||
|
|
||||||
|
string filename;
|
||||||
|
if (pos == string::npos)
|
||||||
|
filename = url.substr(start);
|
||||||
|
else if (pos > start)
|
||||||
|
filename = url.substr(start, pos - start);
|
||||||
|
|
||||||
|
if (filename == "..")
|
||||||
|
level--;
|
||||||
|
else if (!filename.empty() && filename != ".")
|
||||||
|
level++;
|
||||||
|
|
||||||
|
if (level < 0 || pos == string::npos || pos == url.length() - 1)
|
||||||
|
return level;
|
||||||
|
|
||||||
|
start = pos + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
184
src/util/misc.h
Normal file
184
src/util/misc.h
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MISCUTIL
|
||||||
|
#define MISCUTIL
|
||||||
|
|
||||||
|
#include "inclstdint.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
namespace UTILNAMESPACE
|
||||||
|
{
|
||||||
|
void PrintError(const std::string& error);
|
||||||
|
bool GetWord(std::string& data, std::string& word);
|
||||||
|
void ConvertFloatLocale(std::string& strfloat);
|
||||||
|
bool GetHomePath(std::string& homepath);
|
||||||
|
std::string PutSlashAtEnd(const std::string& path);
|
||||||
|
std::string RemoveSlashAtEnd(const std::string& path);
|
||||||
|
std::string PutSlashAtStart(const std::string& path);
|
||||||
|
std::string FileNameExtension(const std::string& path);
|
||||||
|
std::string ToLower(const std::string& in);
|
||||||
|
bool StrToBool(const std::string& data, bool& value);
|
||||||
|
int DirLevel(const std::string& url);
|
||||||
|
|
||||||
|
template <class Value>
|
||||||
|
inline std::string ToString(Value value)
|
||||||
|
{
|
||||||
|
std::string data;
|
||||||
|
std::stringstream valuestream;
|
||||||
|
valuestream << value;
|
||||||
|
valuestream >> data;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string ToString(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
return "true";
|
||||||
|
else
|
||||||
|
return "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string GetErrno()
|
||||||
|
{
|
||||||
|
return strerror(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string GetErrno(int err)
|
||||||
|
{
|
||||||
|
return strerror(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A, class B, class C>
|
||||||
|
inline A Clamp(A value, B min, C max)
|
||||||
|
{
|
||||||
|
return value < max ? (value > min ? value : min) : max;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A, class B>
|
||||||
|
inline A Max(A value1, B value2)
|
||||||
|
{
|
||||||
|
return value1 > value2 ? value1 : value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A, class B, class C>
|
||||||
|
inline A Max(A value1, B value2, C value3)
|
||||||
|
{
|
||||||
|
return (value1 > value2) ? (value1 > value3 ? value1 : value3) : (value2 > value3 ? value2 : value3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A, class B>
|
||||||
|
inline A Min(A value1, B value2)
|
||||||
|
{
|
||||||
|
return value1 < value2 ? value1 : value2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A, class B, class C>
|
||||||
|
inline A Min(A value1, B value2, C value3)
|
||||||
|
{
|
||||||
|
return (value1 < value2) ? (value1 < value3 ? value1 : value3) : (value2 < value3 ? value2 : value3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline T Abs(T value)
|
||||||
|
{
|
||||||
|
return value > 0 ? value : -value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class A, class B>
|
||||||
|
inline A Round(B value)
|
||||||
|
{
|
||||||
|
if (value == 0.0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (value > 0.0)
|
||||||
|
{
|
||||||
|
return (A)(value + 0.5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (A)(value - 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int32_t Round32(float value)
|
||||||
|
{
|
||||||
|
return lroundf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int32_t Round32(double value)
|
||||||
|
{
|
||||||
|
return lround(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int64_t Round64(float value)
|
||||||
|
{
|
||||||
|
return llroundf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int64_t Round64(double value)
|
||||||
|
{
|
||||||
|
return llround(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool StrToInt(const std::string& data, int32_t& value)
|
||||||
|
{
|
||||||
|
return sscanf(data.c_str(), "%" PRIi32, &value) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool StrToInt(const std::string& data, int64_t& value)
|
||||||
|
{
|
||||||
|
return sscanf(data.c_str(), "%" PRIi64, &value) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool HexStrToInt(const std::string& data, int32_t& value)
|
||||||
|
{
|
||||||
|
return sscanf(data.c_str(), "%" PRIx32, &value) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool HexStrToInt(const std::string& data, int64_t& value)
|
||||||
|
{
|
||||||
|
return sscanf(data.c_str(), "%" PRIx64, &value) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool StrToFloat(const std::string& data, float& value)
|
||||||
|
{
|
||||||
|
return sscanf(data.c_str(), "%f", &value) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool StrToFloat(const std::string& data, double& value)
|
||||||
|
{
|
||||||
|
return sscanf(data.c_str(), "%lf", &value) == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace UTILNAMESPACE;
|
||||||
|
|
||||||
|
#endif //MISCUTIL
|
70
src/util/mutex.cpp
Normal file
70
src/util/mutex.cpp
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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 <time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
CMutex::CMutex()
|
||||||
|
{
|
||||||
|
//make a recursive mutex
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
|
pthread_mutexattr_init(&attr);
|
||||||
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||||
|
|
||||||
|
pthread_mutex_init(&m_mutex, &attr);
|
||||||
|
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
|
||||||
|
m_refcount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMutex::~CMutex()
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMutex::Unlock()
|
||||||
|
{
|
||||||
|
m_refcount--;
|
||||||
|
assert(m_refcount >= 0);
|
||||||
|
pthread_mutex_unlock(&m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CMutex::TryLock()
|
||||||
|
{
|
||||||
|
if (pthread_mutex_trylock(&m_mutex) == 0)
|
||||||
|
{
|
||||||
|
m_refcount++;
|
||||||
|
assert(m_refcount > 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CMutex::Lock()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&m_mutex);
|
||||||
|
m_refcount++;
|
||||||
|
assert(m_refcount > 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
40
src/util/mutex.h
Normal file
40
src/util/mutex.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CMUTEX
|
||||||
|
#define CMUTEX
|
||||||
|
|
||||||
|
#include "inclstdint.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
class CMutex
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMutex();
|
||||||
|
~CMutex();
|
||||||
|
bool Lock();
|
||||||
|
void Unlock();
|
||||||
|
bool TryLock();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
pthread_mutex_t m_mutex;
|
||||||
|
int m_refcount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CMUTEX
|
56
src/util/timeutils.cpp
Normal file
56
src/util/timeutils.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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 "timeutils.h"
|
||||||
|
|
||||||
|
void USleep(int64_t usecs, volatile bool* stop /*= NULL*/)
|
||||||
|
{
|
||||||
|
if (usecs <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (stop && usecs > 1000000) //when a pointer to a bool is passed, check it every second and stop when it's true
|
||||||
|
{
|
||||||
|
int64_t now = GetTimeUs();
|
||||||
|
int64_t end = now + usecs;
|
||||||
|
|
||||||
|
while (now < end)
|
||||||
|
{
|
||||||
|
struct timespec sleeptime = {};
|
||||||
|
|
||||||
|
if (*stop)
|
||||||
|
return;
|
||||||
|
else if (end - now >= 1000000)
|
||||||
|
sleeptime.tv_sec = 1;
|
||||||
|
else
|
||||||
|
sleeptime.tv_nsec = ((end - now) % 1000000) * 1000;
|
||||||
|
|
||||||
|
nanosleep(&sleeptime, NULL);
|
||||||
|
now = GetTimeUs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct timespec sleeptime;
|
||||||
|
sleeptime.tv_sec = usecs / 1000000;
|
||||||
|
sleeptime.tv_nsec = (usecs % 1000000) * 1000;
|
||||||
|
|
||||||
|
nanosleep(&sleeptime, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
49
src/util/timeutils.h
Normal file
49
src/util/timeutils.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* boblight
|
||||||
|
* Copyright (C) Bob 2009
|
||||||
|
*
|
||||||
|
* boblight 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.
|
||||||
|
*
|
||||||
|
* boblight 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TIMEUTILS
|
||||||
|
#define TIMEUTILS
|
||||||
|
|
||||||
|
#include "inclstdint.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
inline int64_t GetTimeUs()
|
||||||
|
{
|
||||||
|
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
|
||||||
|
struct timespec time;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &time);
|
||||||
|
return ((int64_t)time.tv_sec * 1000000LL) + (int64_t)(time.tv_nsec + 500) / 1000LL;
|
||||||
|
#else
|
||||||
|
struct timeval time;
|
||||||
|
gettimeofday(&time, NULL);
|
||||||
|
return ((int64_t)time.tv_sec * 1000000LL) + (int64_t)time.tv_usec;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline T GetTimeSec()
|
||||||
|
{
|
||||||
|
return (T)GetTimeUs() / (T)1000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USleep(int64_t usecs, volatile bool* stop = NULL);
|
||||||
|
|
||||||
|
#endif //TIMEUTILS
|
21
wscript
21
wscript
|
@ -16,10 +16,15 @@ def configure(conf):
|
||||||
conf.load('compiler_cxx')
|
conf.load('compiler_cxx')
|
||||||
|
|
||||||
conf.check(header_name='jack/jack.h')
|
conf.check(header_name='jack/jack.h')
|
||||||
|
conf.check(header_name='fftw3.h')
|
||||||
|
conf.check(header_name='samplerate.h')
|
||||||
|
|
||||||
conf.check(lib='pthread', uselib_store='pthread', mandatory=False)
|
conf.check(lib='fftw3', uselib_store='fftw3')
|
||||||
conf.check(lib='m', uselib_store='m', mandatory=False)
|
conf.check(lib='fftw3f', uselib_store='fftw3f')
|
||||||
conf.check(lib='jack', uselib_store='jack')
|
conf.check(lib='jack', uselib_store='jack')
|
||||||
|
conf.check(lib='samplerate', uselib_store='samplerate')
|
||||||
|
conf.check(lib='m', uselib_store='m', mandatory=False)
|
||||||
|
conf.check(lib='pthread', uselib_store='pthread', mandatory=False)
|
||||||
|
|
||||||
conf.check(function_name='clock_gettime', header_name='time.h', mandatory=False)
|
conf.check(function_name='clock_gettime', header_name='time.h', mandatory=False)
|
||||||
conf.check(function_name='clock_gettime', header_name='time.h', lib='rt', uselib_store='rt', mandatory=False,
|
conf.check(function_name='clock_gettime', header_name='time.h', lib='rt', uselib_store='rt', mandatory=False,
|
||||||
|
@ -28,8 +33,16 @@ def configure(conf):
|
||||||
conf.write_config_header('config.h')
|
conf.write_config_header('config.h')
|
||||||
|
|
||||||
def build(bld):
|
def build(bld):
|
||||||
bld.program(source='src/main.cpp',
|
bld.program(source='src/main.cpp\
|
||||||
use=['m','pthread','rt', 'jack'],
|
src/bitvis.cpp\
|
||||||
|
src/jackclient.cpp\
|
||||||
|
src/util/log.cpp\
|
||||||
|
src/util/misc.cpp\
|
||||||
|
src/util/mutex.cpp\
|
||||||
|
src/util/timeutils.cpp\
|
||||||
|
src/util/condition.cpp\
|
||||||
|
src/fft.cpp',
|
||||||
|
use=['m','pthread','rt', 'jack', 'fftw3', 'fftw3f', 'samplerate'],
|
||||||
includes='./src',
|
includes='./src',
|
||||||
cxxflags='-Wall -g -DUTILNAMESPACE=BitVisUtil',
|
cxxflags='-Wall -g -DUTILNAMESPACE=BitVisUtil',
|
||||||
target='bitvis')
|
target='bitvis')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue