added: bitvis (WIP)

This commit is contained in:
Bob 2012-11-04 21:29:06 +01:00
parent 8eb8117da3
commit 4d4f7c585c
22 changed files with 2060 additions and 4 deletions

254
src/bitvis.cpp Normal file
View 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
View 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
View 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
View file

@ -0,0 +1 @@
#include "../build/config.h"

100
src/fft.cpp Normal file
View 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
View 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
View 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
View 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

View file

@ -16,7 +16,16 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bitvis.h"
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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -16,10 +16,15 @@ def configure(conf):
conf.load('compiler_cxx')
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='m', uselib_store='m', mandatory=False)
conf.check(lib='fftw3', uselib_store='fftw3')
conf.check(lib='fftw3f', uselib_store='fftw3f')
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', lib='rt', uselib_store='rt', mandatory=False,
@ -28,8 +33,16 @@ def configure(conf):
conf.write_config_header('config.h')
def build(bld):
bld.program(source='src/main.cpp',
use=['m','pthread','rt', 'jack'],
bld.program(source='src/main.cpp\
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',
cxxflags='-Wall -g -DUTILNAMESPACE=BitVisUtil',
target='bitvis')