diff --git a/src/bitvis.cpp b/src/bitvis.cpp
new file mode 100644
index 0000000..9d4b1ce
--- /dev/null
+++ b/src/bitvis.cpp
@@ -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 .
+ */
+
+#include "util/inclstdint.h"
+#include "bitvis.h"
+#include "util/log.h"
+#include "util/misc.h"
+#include "util/timeutils.h"
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+#define CONNECTINTERVAL 10000000
+
+CBitVis::CBitVis(int argc, char *argv[])
+{
+ g_printdebuglevel = true;
+ m_stop = false;
+ m_buf = NULL;
+ m_bufsize = 0;
+ m_fftbuf = NULL;
+ m_samplecounter = 0;
+ m_nrffts = 0;
+}
+
+CBitVis::~CBitVis()
+{
+}
+
+void CBitVis::Setup()
+{
+ //init the logfile
+ SetLogFile("bitvis.log");
+
+ SetupSignals();
+
+ jack_set_error_function(JackError);
+ jack_set_info_function(JackInfo);
+}
+
+void CBitVis::SetupSignals()
+{
+ m_signalfd = -1;
+
+ sigset_t sigset;
+ if (sigemptyset(&sigset) == -1)
+ {
+ LogError("sigemptyset: %s", GetErrno().c_str());
+ return;
+ }
+
+ if (sigaddset(&sigset, SIGTERM) == -1)
+ {
+ LogError("adding SIGTERM: %s", GetErrno().c_str());
+ return;
+ }
+
+ if (sigaddset(&sigset, SIGINT) == -1)
+ {
+ LogError("adding SIGINT: %s", GetErrno().c_str());
+ return;
+ }
+
+ //create a file descriptor that will catch SIGTERM and SIGINT
+ m_signalfd = signalfd(-1, &sigset, SFD_NONBLOCK);
+ if (m_signalfd == -1)
+ {
+ LogError("signalfd: %s", GetErrno().c_str());
+ }
+ else
+ {
+ //block SIGTERM and SIGINT
+ if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1)
+ LogError("sigpocmask: %s", GetErrno().c_str());
+ }
+
+ if (sigemptyset(&sigset) == -1)
+ {
+ LogError("sigemptyset: %s", GetErrno().c_str());
+ return;
+ }
+
+ if (sigaddset(&sigset, SIGPIPE) == -1)
+ {
+ LogError("adding SIGPIPE: %s", GetErrno().c_str());
+ return;
+ }
+
+ //libjack throws SIGPIPE a lot, block it
+ if (sigprocmask(SIG_BLOCK, &sigset, NULL) == -1)
+ LogError("sigpocmask: %s", GetErrno().c_str());
+}
+
+void CBitVis::Process()
+{
+ int64_t lastconnect = GetTimeUs() - CONNECTINTERVAL;
+
+ while (!m_stop)
+ {
+ if (m_jackclient.ExitStatus())
+ {
+ LogError("Jack client exited with code %i reason: \"%s\"",
+ (int)m_jackclient.ExitStatus(), m_jackclient.ExitReason().c_str());
+ m_jackclient.Disconnect();
+ }
+
+ if (!m_jackclient.IsConnected() && GetTimeUs() - lastconnect > CONNECTINTERVAL)
+ {
+ m_jackclient.Connect();
+ lastconnect = GetTimeUs();
+ }
+
+ uint8_t msg;
+ while ((msg = m_jackclient.GetMessage()) != MsgNone)
+ LogDebug("got message %s from jack client", MsgToString(msg));
+
+ if (m_jackclient.IsConnected())
+ {
+ ProcessAudio();
+ }
+ else
+ {
+ sleep(1);
+ }
+
+ ProcessSignalfd();
+ }
+
+ m_jackclient.Disconnect();
+}
+
+void CBitVis::ProcessSignalfd()
+{
+ signalfd_siginfo siginfo;
+ int returnv = read(m_signalfd, &siginfo, sizeof(siginfo));
+ if (returnv == -1 && errno != EAGAIN)
+ {
+ LogError("reading signals fd: %s", GetErrno().c_str());
+ if (errno != EINTR)
+ {
+ close(m_signalfd);
+ m_signalfd = -1;
+ }
+ }
+ else if (returnv > 0)
+ {
+ if (siginfo.ssi_signo == SIGTERM || siginfo.ssi_signo == SIGINT)
+ {
+ Log("caught %s, exiting", siginfo.ssi_signo == SIGTERM ? "SIGTERM" : "SIGINT");
+ m_stop = true;
+ }
+ else
+ {
+ LogDebug("caught signal %i", siginfo.ssi_signo);
+ }
+ }
+}
+
+void CBitVis::ProcessAudio()
+{
+ const int bins = 1024;
+ const int lines = 46;
+ int samplerate;
+ int samples;
+ if ((samples = m_jackclient.GetAudio(m_buf, m_bufsize, samplerate)) > 0)
+ {
+ m_fft.Allocate(bins * 2);
+ if (!m_fftbuf)
+ {
+ m_fftbuf = new float[bins];
+ memset(m_fftbuf, 0, bins * sizeof(float));
+ }
+
+ int additions = 0;
+ for (int i = 1; i < lines; i++)
+ additions += i;
+
+ const int maxbin = Round32(15000.0f / samplerate * bins * 2.0f);
+
+ float increase = (float)(maxbin - lines) / additions;
+
+ for (int i = 0; i < samples; i++)
+ {
+ m_fft.AddSample(m_buf[i]);
+
+ m_samplecounter++;
+ if (m_samplecounter >= 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);
+}
+
diff --git a/src/bitvis.h b/src/bitvis.h
new file mode 100644
index 0000000..8eb2969
--- /dev/null
+++ b/src/bitvis.h
@@ -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 .
+ */
+
+#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
diff --git a/src/clientmessage.h b/src/clientmessage.h
new file mode 100644
index 0000000..6177c22
--- /dev/null
+++ b/src/clientmessage.h
@@ -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 .
+ */
+
+#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
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..53e0bd6
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1 @@
+#include "../build/config.h"
diff --git a/src/fft.cpp b/src/fft.cpp
new file mode 100644
index 0000000..38cc9b9
--- /dev/null
+++ b/src/fft.cpp
@@ -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 .
+ */
+
+#include
+#include //TODO: REMOVE
+#include
+
+#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;
+}
+
diff --git a/src/fft.h b/src/fft.h
new file mode 100644
index 0000000..a574280
--- /dev/null
+++ b/src/fft.h
@@ -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 .
+ */
+
+#ifndef FFT_H
+#define FFT_H
+
+#include
+#include
+
+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
diff --git a/src/jackclient.cpp b/src/jackclient.cpp
new file mode 100644
index 0000000..3fe4050
--- /dev/null
+++ b/src/jackclient.cpp
@@ -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 .
+ */
+
+#ifndef _GNU_SOURCE
+ #define _GNU_SOURCE //for pipe2
+#endif //_GNU_SOURCE
+#include
+#include
+#include
+#include
+#include
+
+#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());
+}
+
diff --git a/src/jackclient.h b/src/jackclient.h
new file mode 100644
index 0000000..85812db
--- /dev/null
+++ b/src/jackclient.h
@@ -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 .
+ */
+
+#ifndef JACKCLIENT_H
+#define JACKCLIENT_H
+
+#include
+#include
+#include
+#include
+
+#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
diff --git a/src/main.cpp b/src/main.cpp
index d5f0854..77d2c30 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -16,7 +16,16 @@
* with this program. If not, see .
*/
+#include "bitvis.h"
+
int main (int argc, char *argv[])
{
+ CBitVis bitvis(argc, argv);
+
+ bitvis.Setup();
+ bitvis.Process();
+ bitvis.Cleanup();
+
+ return 0;
}
diff --git a/src/util/condition.cpp b/src/util/condition.cpp
new file mode 100644
index 0000000..74fdf2b
--- /dev/null
+++ b/src/util/condition.cpp
@@ -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 .
+ */
+
+#include
+#include
+#include
+#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;
+}
diff --git a/src/util/condition.h b/src/util/condition.h
new file mode 100644
index 0000000..89a8eda
--- /dev/null
+++ b/src/util/condition.h
@@ -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 .
+ */
+
+#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
+ 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
diff --git a/src/util/inclstdint.h b/src/util/inclstdint.h
new file mode 100644
index 0000000..0b6804f
--- /dev/null
+++ b/src/util/inclstdint.h
@@ -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 .
+ */
+
+//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
+ #include
+#endif
diff --git a/src/util/lock.h b/src/util/lock.h
new file mode 100644
index 0000000..66259da
--- /dev/null
+++ b/src/util/lock.h
@@ -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 .
+ */
+
+#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
\ No newline at end of file
diff --git a/src/util/log.cpp b/src/util/log.cpp
new file mode 100644
index 0000000..2e0ab0f
--- /dev/null
+++ b/src/util/log.cpp
@@ -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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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* 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::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;
+ 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();
+}
diff --git a/src/util/log.h b/src/util/log.h
new file mode 100644
index 0000000..67d6b01
--- /dev/null
+++ b/src/util/log.h
@@ -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 .
+ */
+
+#ifndef LOG
+#define LOG
+
+#include
+
+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
diff --git a/src/util/misc.cpp b/src/util/misc.cpp
new file mode 100644
index 0000000..751f7f7
--- /dev/null
+++ b/src/util/misc.cpp
@@ -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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#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;
+ }
+ }
+}
diff --git a/src/util/misc.h b/src/util/misc.h
new file mode 100644
index 0000000..80254f4
--- /dev/null
+++ b/src/util/misc.h
@@ -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 .
+ */
+
+#ifndef MISCUTIL
+#define MISCUTIL
+
+#include "inclstdint.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+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
+ 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
+ inline A Clamp(A value, B min, C max)
+ {
+ return value < max ? (value > min ? value : min) : max;
+ }
+
+ template
+ inline A Max(A value1, B value2)
+ {
+ return value1 > value2 ? value1 : value2;
+ }
+
+ template
+ inline A Max(A value1, B value2, C value3)
+ {
+ return (value1 > value2) ? (value1 > value3 ? value1 : value3) : (value2 > value3 ? value2 : value3);
+ }
+
+ template
+ inline A Min(A value1, B value2)
+ {
+ return value1 < value2 ? value1 : value2;
+ }
+
+ template
+ inline A Min(A value1, B value2, C value3)
+ {
+ return (value1 < value2) ? (value1 < value3 ? value1 : value3) : (value2 < value3 ? value2 : value3);
+ }
+
+ template
+ inline T Abs(T value)
+ {
+ return value > 0 ? value : -value;
+ }
+
+ template
+ 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
diff --git a/src/util/mutex.cpp b/src/util/mutex.cpp
new file mode 100644
index 0000000..ed54127
--- /dev/null
+++ b/src/util/mutex.cpp
@@ -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 .
+ */
+
+#include
+#include
+#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;
+}
+
diff --git a/src/util/mutex.h b/src/util/mutex.h
new file mode 100644
index 0000000..c1e5112
--- /dev/null
+++ b/src/util/mutex.h
@@ -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 .
+ */
+
+#ifndef CMUTEX
+#define CMUTEX
+
+#include "inclstdint.h"
+
+#include
+
+class CMutex
+{
+ public:
+ CMutex();
+ ~CMutex();
+ bool Lock();
+ void Unlock();
+ bool TryLock();
+
+ protected:
+ pthread_mutex_t m_mutex;
+ int m_refcount;
+};
+
+#endif //CMUTEX
diff --git a/src/util/timeutils.cpp b/src/util/timeutils.cpp
new file mode 100644
index 0000000..b5f67f5
--- /dev/null
+++ b/src/util/timeutils.cpp
@@ -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 .
+ */
+
+#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);
+ }
+}
+
diff --git a/src/util/timeutils.h b/src/util/timeutils.h
new file mode 100644
index 0000000..07d2d88
--- /dev/null
+++ b/src/util/timeutils.h
@@ -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 .
+ */
+
+#ifndef TIMEUTILS
+#define TIMEUTILS
+
+#include "inclstdint.h"
+#include "config.h"
+
+#include
+#include
+
+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
+inline T GetTimeSec()
+{
+ return (T)GetTimeUs() / (T)1000000.0;
+}
+
+void USleep(int64_t usecs, volatile bool* stop = NULL);
+
+#endif //TIMEUTILS
diff --git a/wscript b/wscript
index 379b2a4..e42c6bd 100644
--- a/wscript
+++ b/wscript
@@ -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')