commit 5146a570ecbd19398146db88c3184e750661ca06
parent 4d5818e453bba300f7b5440365a1673bf589fcec
Author: fundamental <mark.d.mccurry@gmail.com>
Date: Sat, 25 Oct 2014 12:24:20 -0400
Merge remote-tracking branch 'origin/master'
Diffstat:
9 files changed, 302 insertions(+), 113 deletions(-)
diff --git a/src/DSP/FFTwrapper.h b/src/DSP/FFTwrapper.h
@@ -48,5 +48,24 @@ class FFTwrapper
fftw_plan planfftw, planfftw_inv;
};
+/*
+ * The "std::polar" template has no clear definition for the range of
+ * the input parameters, and some C++ standard library implementations
+ * don't accept negative amplitude among others. Define our own
+ * FFTpolar template, which works like we expect it to.
+ */
+template<class _Tp>
+std::complex<_Tp>
+FFTpolar(const _Tp& __rho, const _Tp& __theta = _Tp(0))
+{
+ _Tp __x = __rho * cos(__theta);
+ if (isnan(__x))
+ __x = 0;
+ _Tp __y = __rho * sin(__theta);
+ if (isnan(__y))
+ __y = 0;
+ return std::complex<_Tp>(__x, __y);
+}
+
void FFT_cleanup();
#endif
diff --git a/src/Effects/Distorsion.cpp b/src/Effects/Distorsion.cpp
@@ -138,7 +138,7 @@ void Distorsion::setvolume(unsigned char _Pvolume)
void Distorsion::setlpf(unsigned char _Plpf)
{
Plpf = _Plpf;
- float fr = expf(powf(Plpf / 127.0f, 0.5f) * logf(25000.0f)) + 40.0f;
+ float fr = expf(sqrtf(Plpf / 127.0f) * logf(25000.0f)) + 40.0f;
lpfl->setfreq(fr);
lpfr->setfreq(fr);
}
@@ -146,7 +146,7 @@ void Distorsion::setlpf(unsigned char _Plpf)
void Distorsion::sethpf(unsigned char _Phpf)
{
Phpf = _Phpf;
- float fr = expf(powf(Phpf / 127.0f, 0.5f) * logf(25000.0f)) + 20.0f;
+ float fr = expf(sqrtf(Phpf / 127.0f) * logf(25000.0f)) + 20.0f;
hpfl->setfreq(fr);
hpfr->setfreq(fr);
}
diff --git a/src/Effects/Reverb.cpp b/src/Effects/Reverb.cpp
@@ -253,7 +253,7 @@ void Reverb::sethpf(unsigned char _Phpf)
if(Phpf == 0) { //No HighPass
memory.dealloc(hpf);
} else {
- float fr = expf(powf(Phpf / 127.0f, 0.5f) * logf(10000.0f)) + 20.0f;
+ float fr = expf(sqrtf(Phpf / 127.0f) * logf(10000.0f)) + 20.0f;
if(hpf == NULL)
hpf = memory.alloc<AnalogFilter>(3, fr, 1, 0, samplerate, buffersize);
else
@@ -267,7 +267,7 @@ void Reverb::setlpf(unsigned char _Plpf)
if(Plpf == 127) { //No LowPass
memory.dealloc(lpf);
} else {
- float fr = expf(powf(Plpf / 127.0f, 0.5f) * logf(25000.0f)) + 40.0f;
+ float fr = expf(sqrtf(Plpf / 127.0f) * logf(25000.0f)) + 40.0f;
if(!lpf)
lpf = memory.alloc<AnalogFilter>(2, fr, 1, 0, samplerate, buffersize);
else
diff --git a/src/Nio/OssEngine.cpp b/src/Nio/OssEngine.cpp
@@ -33,13 +33,153 @@
#include <sys/ioctl.h>
#include <unistd.h>
#include <iostream>
+#include <signal.h>
#include "InMgr.h"
using namespace std;
+/*
+ * The following statemachine converts MIDI commands to USB MIDI
+ * packets, derived from Linux's usbmidi.c, which was written by
+ * "Clemens Ladisch". It is used to figure out when a MIDI command is
+ * complete, without having to read the first byte of the next MIDI
+ * command. This is useful when connecting to so-called system PIPEs
+ * and FIFOs. See "man mkfifo".
+ *
+ * Return values:
+ * 0: No command
+ * Else: Command is complete
+ */
+static unsigned char
+OssMidiParse(struct OssMidiParse &midi_parse,
+ unsigned char cn, unsigned char b)
+{
+ unsigned char p0 = (cn << 4);
+
+ if(b >= 0xf8) {
+ midi_parse.temp_0[0] = p0 | 0x0f;
+ midi_parse.temp_0[1] = b;
+ midi_parse.temp_0[2] = 0;
+ midi_parse.temp_0[3] = 0;
+ midi_parse.temp_cmd = midi_parse.temp_0;
+ return (1);
+
+ } else if(b >= 0xf0) {
+ switch (b) {
+ case 0xf0: /* system exclusive begin */
+ midi_parse.temp_1[1] = b;
+ midi_parse.state = OSSMIDI_ST_SYSEX_1;
+ break;
+ case 0xf1: /* MIDI time code */
+ case 0xf3: /* song select */
+ midi_parse.temp_1[1] = b;
+ midi_parse.state = OSSMIDI_ST_1PARAM;
+ break;
+ case 0xf2: /* song position pointer */
+ midi_parse.temp_1[1] = b;
+ midi_parse.state = OSSMIDI_ST_2PARAM_1;
+ break;
+ case 0xf4: /* unknown */
+ case 0xf5: /* unknown */
+ midi_parse.state = OSSMIDI_ST_UNKNOWN;
+ break;
+ case 0xf6: /* tune request */
+ midi_parse.temp_1[0] = p0 | 0x05;
+ midi_parse.temp_1[1] = 0xf6;
+ midi_parse.temp_1[2] = 0;
+ midi_parse.temp_1[3] = 0;
+ midi_parse.temp_cmd = midi_parse.temp_1;
+ midi_parse.state = OSSMIDI_ST_UNKNOWN;
+ return (1);
+
+ case 0xf7: /* system exclusive end */
+ switch (midi_parse.state) {
+ case OSSMIDI_ST_SYSEX_0:
+ midi_parse.temp_1[0] = p0 | 0x05;
+ midi_parse.temp_1[1] = 0xf7;
+ midi_parse.temp_1[2] = 0;
+ midi_parse.temp_1[3] = 0;
+ midi_parse.temp_cmd = midi_parse.temp_1;
+ midi_parse.state = OSSMIDI_ST_UNKNOWN;
+ return (1);
+ case OSSMIDI_ST_SYSEX_1:
+ midi_parse.temp_1[0] = p0 | 0x06;
+ midi_parse.temp_1[2] = 0xf7;
+ midi_parse.temp_1[3] = 0;
+ midi_parse.temp_cmd = midi_parse.temp_1;
+ midi_parse.state = OSSMIDI_ST_UNKNOWN;
+ return (1);
+ case OSSMIDI_ST_SYSEX_2:
+ midi_parse.temp_1[0] = p0 | 0x07;
+ midi_parse.temp_1[3] = 0xf7;
+ midi_parse.temp_cmd = midi_parse.temp_1;
+ midi_parse.state = OSSMIDI_ST_UNKNOWN;
+ return (1);
+ }
+ midi_parse.state = OSSMIDI_ST_UNKNOWN;
+ break;
+ }
+ } else if(b >= 0x80) {
+ midi_parse.temp_1[1] = b;
+ if((b >= 0xc0) && (b <= 0xdf)) {
+ midi_parse.state = OSSMIDI_ST_1PARAM;
+ } else {
+ midi_parse.state = OSSMIDI_ST_2PARAM_1;
+ }
+ } else { /* b < 0x80 */
+ switch (midi_parse.state) {
+ case OSSMIDI_ST_1PARAM:
+ if(midi_parse.temp_1[1] < 0xf0) {
+ p0 |= midi_parse.temp_1[1] >> 4;
+ } else {
+ p0 |= 0x02;
+ midi_parse.state = OSSMIDI_ST_UNKNOWN;
+ }
+ midi_parse.temp_1[0] = p0;
+ midi_parse.temp_1[2] = b;
+ midi_parse.temp_1[3] = 0;
+ midi_parse.temp_cmd = midi_parse.temp_1;
+ return (1);
+ case OSSMIDI_ST_2PARAM_1:
+ midi_parse.temp_1[2] = b;
+ midi_parse.state = OSSMIDI_ST_2PARAM_2;
+ break;
+ case OSSMIDI_ST_2PARAM_2:
+ if(midi_parse.temp_1[1] < 0xf0) {
+ p0 |= midi_parse.temp_1[1] >> 4;
+ midi_parse.state = OSSMIDI_ST_2PARAM_1;
+ } else {
+ p0 |= 0x03;
+ midi_parse.state = OSSMIDI_ST_UNKNOWN;
+ }
+ midi_parse.temp_1[0] = p0;
+ midi_parse.temp_1[3] = b;
+ midi_parse.temp_cmd = midi_parse.temp_1;
+ return (1);
+ case OSSMIDI_ST_SYSEX_0:
+ midi_parse.temp_1[1] = b;
+ midi_parse.state = OSSMIDI_ST_SYSEX_1;
+ break;
+ case OSSMIDI_ST_SYSEX_1:
+ midi_parse.temp_1[2] = b;
+ midi_parse.state = OSSMIDI_ST_SYSEX_2;
+ break;
+ case OSSMIDI_ST_SYSEX_2:
+ midi_parse.temp_1[0] = p0 | 0x04;
+ midi_parse.temp_1[3] = b;
+ midi_parse.temp_cmd = midi_parse.temp_1;
+ midi_parse.state = OSSMIDI_ST_SYSEX_0;
+ return (1);
+ default:
+ break;
+ }
+ }
+ return (0);
+}
+
OssEngine::OssEngine()
- :AudioOut(), engThread(NULL)
+ :AudioOut(), audioThread(NULL), midiThread(NULL)
{
name = "OSS";
@@ -48,6 +188,7 @@ OssEngine::OssEngine()
audio.smps = new short[synth->buffersize * 2];
memset(audio.smps, 0, synth->bufferbytes);
+ memset(&midi.state, 0, sizeof(midi.state));
}
OssEngine::~OssEngine()
@@ -67,11 +208,12 @@ bool OssEngine::openAudio()
int snd_format = AFMT_S16_LE;
int snd_samplerate = synth->samplerate;
- const char *device = config.cfg.LinuxOSSWaveOutDev;
- if(getenv("DSP_DEVICE"))
- device = getenv("DSP_DEVICE");
+ const char *device = getenv("DSP_DEVICE");
+ if(device == NULL)
+ device = config.cfg.LinuxOSSWaveOutDev;
- audio.handle = open(device, O_WRONLY, 0);
+ /* NOTE: PIPEs and FIFOs can block when opening them */
+ audio.handle = open(device, O_WRONLY, O_NONBLOCK);
if(audio.handle == -1) {
cerr << "ERROR - I can't open the "
<< device << '.' << endl;
@@ -84,13 +226,11 @@ bool OssEngine::openAudio()
ioctl(audio.handle, SNDCTL_DSP_SAMPLESIZE, &snd_bitsize);
ioctl(audio.handle, SNDCTL_DSP_SETFRAGMENT, &snd_fragment);
- if(!getMidiEn()) {
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- engThread = new pthread_t;
- pthread_create(engThread, &attr, _thread, this);
- }
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ audioThread = new pthread_t;
+ pthread_create(audioThread, &attr, _audioThreadCb, this);
return true;
}
@@ -102,12 +242,12 @@ void OssEngine::stopAudio()
return;
audio.handle = -1;
- if(!getMidiEn() && engThread)
- pthread_join(*engThread, NULL);
- delete engThread;
- engThread = NULL;
-
+ /* close handle first, so that write() exits */
close(handle);
+
+ pthread_join(*audioThread, NULL);
+ delete audioThread;
+ audioThread = NULL;
}
bool OssEngine::Start()
@@ -165,19 +305,22 @@ bool OssEngine::openMidi()
if(handle != -1)
return true; //already open
- handle = open(config.cfg.LinuxOSSSeqInDev, O_RDONLY, 0);
+ const char *device = getenv("MIDI_DEVICE");
+ if(device == NULL)
+ device = config.cfg.LinuxOSSSeqInDev;
+
+ /* NOTE: PIPEs and FIFOs can block when opening them */
+ handle = open(device, O_RDONLY, O_NONBLOCK);
if(-1 == handle)
return false;
midi.handle = handle;
- if(!getAudioEn()) {
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- engThread = new pthread_t;
- pthread_create(engThread, &attr, _thread, this);
- }
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ midiThread = new pthread_t;
+ pthread_create(midiThread, &attr, _midiThreadCb, this);
return true;
}
@@ -190,96 +333,106 @@ void OssEngine::stopMidi()
midi.handle = -1;
- if(!getAudioEn() && engThread) {
- pthread_join(*engThread, NULL);
- delete engThread;
- engThread = NULL;
- }
-
+ /* close handle first, so that read() exits */
close(handle);
+
+ pthread_join(*midiThread, NULL);
+ delete midiThread;
+ midiThread = NULL;
}
-void *OssEngine::_thread(void *arg)
+void *OssEngine::_audioThreadCb(void *arg)
{
- return (static_cast<OssEngine *>(arg))->thread();
+ return (static_cast<OssEngine *>(arg))->audioThreadCb();
}
-void *OssEngine::thread()
+void *OssEngine::_midiThreadCb(void *arg)
{
- unsigned char tmp[4] = {0, 0, 0, 0};
+ return (static_cast<OssEngine *>(arg))->midiThreadCb();
+}
+
+void *OssEngine::audioThreadCb()
+{
+ /*
+ * In case the audio device is a PIPE/FIFO,
+ * we need to ignore any PIPE signals:
+ */
+ signal(SIGPIPE, SIG_IGN);
+
set_realtime();
- while(getAudioEn() || getMidiEn()) {
- if(getAudioEn()) {
- const Stereo<float *> smps = getNext();
-
- float l, r;
- for(int i = 0; i < synth->buffersize; ++i) {
- l = smps.l[i];
- r = smps.r[i];
-
- if(l < -1.0f)
- l = -1.0f;
- else
+ while(getAudioEn()) {
+ const Stereo<float *> smps = getNext();
+
+ float l, r;
+ for(int i = 0; i < synth->buffersize; ++i) {
+ l = smps.l[i];
+ r = smps.r[i];
+
+ if(l < -1.0f)
+ l = -1.0f;
+ else
if(l > 1.0f)
l = 1.0f;
- if(r < -1.0f)
- r = -1.0f;
- else
+ if(r < -1.0f)
+ r = -1.0f;
+ else
if(r > 1.0f)
r = 1.0f;
- audio.smps[i * 2] = (short int) (l * 32767.0f);
- audio.smps[i * 2 + 1] = (short int) (r * 32767.0f);
- }
- int handle = audio.handle;
- if(handle != -1)
- write(handle, audio.smps, synth->buffersize * 4); // *2 because is 16 bit, again * 2 because is stereo
- else
- break;
+ audio.smps[i * 2] = (short int) (l * 32767.0f);
+ audio.smps[i * 2 + 1] = (short int) (r * 32767.0f);
}
- //Collect up to 30 midi events
- for(int k = 0; k < 30 && getMidiEn(); ++k) {
- static char escaped;
-
- memset(tmp, 0, 4);
-
- if(escaped) {
- tmp[0] = escaped;
- escaped = 0;
- }
- else {
- getMidi(tmp);
- if(!(tmp[0] & 0x80))
- continue;
- }
- getMidi(tmp + 1);
- if(tmp[1] & 0x80) {
- escaped = tmp[1];
- tmp[1] = 0;
- }
- else {
- getMidi(tmp + 2);
- if(tmp[2] & 0x80) {
- escaped = tmp[2];
- tmp[2] = 0;
- }
- else {
- getMidi(tmp + 3);
- if(tmp[3] & 0x80) {
- escaped = tmp[3];
- tmp[3] = 0;
- }
- }
- }
- midiProcess(tmp[0], tmp[1], tmp[2]);
- }
+ int error;
+ do {
+ /* make a copy of handle, in case of OSS audio disable */
+ int handle = audio.handle;
+ if(handle == -1)
+ goto done;
+ /* 2x because is 16 bit, again 2x because it is stereo */
+ error = write(handle, audio.smps, synth->buffersize * 4);
+ } while (error == -1 && errno == EINTR);
+
+ if(error == -1)
+ goto done;
}
+done:
pthread_exit(NULL);
return NULL;
}
-void OssEngine::getMidi(unsigned char *midiPtr)
+void *OssEngine::midiThreadCb()
{
- read(midi.handle, midiPtr, 1);
+ /*
+ * In case the MIDI device is a PIPE/FIFO,
+ * we need to ignore any PIPE signals:
+ */
+ signal(SIGPIPE, SIG_IGN);
+ set_realtime();
+ while(getMidiEn()) {
+ unsigned char tmp;
+ int error;
+ do {
+ /* make a copy of handle, in case of OSS MIDI disable */
+ int handle = midi.handle;
+ if(handle == -1)
+ goto done;
+ error = read(handle, &tmp, 1);
+ } while (error == -1 && errno == EINTR);
+
+ /* check that we got one byte */
+ if(error != 1)
+ goto done;
+
+ /* feed MIDI byte into statemachine */
+ if(OssMidiParse(midi.state, 0, tmp)) {
+ /* we got a complete MIDI command */
+ midiProcess(midi.state.temp_cmd[1],
+ midi.state.temp_cmd[2],
+ midi.state.temp_cmd[3]);
+ }
+ }
+done:
+ pthread_exit(NULL);
+ return NULL;
}
diff --git a/src/Nio/OssEngine.h b/src/Nio/OssEngine.h
@@ -28,6 +28,20 @@
#include "AudioOut.h"
#include "MidiIn.h"
+struct OssMidiParse {
+ unsigned char *temp_cmd;
+ unsigned char temp_0[4];
+ unsigned char temp_1[4];
+ unsigned char state;
+#define OSSMIDI_ST_UNKNOWN 0 /* scan for command */
+#define OSSMIDI_ST_1PARAM 1
+#define OSSMIDI_ST_2PARAM_1 2
+#define OSSMIDI_ST_2PARAM_2 3
+#define OSSMIDI_ST_SYSEX_0 4
+#define OSSMIDI_ST_SYSEX_1 5
+#define OSSMIDI_ST_SYSEX_2 6
+};
+
class OssEngine:public AudioOut, MidiIn
{
public:
@@ -43,13 +57,16 @@ class OssEngine:public AudioOut, MidiIn
void setMidiEn(bool nval);
bool getMidiEn() const;
-
protected:
- void *thread();
- static void *_thread(void *arg);
+ void *audioThreadCb();
+ static void *_audioThreadCb(void *arg);
+
+ void *midiThreadCb();
+ static void *_midiThreadCb(void *arg);
private:
- pthread_t *engThread;
+ pthread_t *audioThread;
+ pthread_t *midiThread;
//Audio
bool openAudio();
@@ -64,9 +81,9 @@ class OssEngine:public AudioOut, MidiIn
//Midi
bool openMidi();
void stopMidi();
- void getMidi(unsigned char *midiPtr);
struct midi {
+ struct OssMidiParse state;
int handle;
bool en;
bool run;
diff --git a/src/Params/PADnoteParameters.cpp b/src/Params/PADnoteParameters.cpp
@@ -137,7 +137,7 @@ static rtosc::Ports localPorts =
d.reply(d.loc, "i", realbw);
delete[] tmp;}},
{"sample#64:ifb", rDoc("Nothing to see here"), 0,
- [](const char *m, rtosc::RtData d)
+ [](const char *m, rtosc::RtData &d)
{
PADnoteParameters *p = (PADnoteParameters*)d.obj;
const char *mm = m;
@@ -796,7 +796,7 @@ void PADnoteParameters::sampleGenerator(PADnoteParameters::callback cb,
newsample.smp[0] = 0.0f;
for(int i = 1; i < spectrumsize; ++i) //randomize the phases
- fftfreqs[i] = std::polar(spectrum[i], (float)RND * 2 * PI);
+ fftfreqs[i] = FFTpolar(spectrum[i], (float)RND * 2 * PI);
//that's all; here is the only ifft for the whole sample;
//no windows are used ;-)
fft->freqs2smps(fftfreqs, newsample.smp);
diff --git a/src/Synth/OscilGen.cpp b/src/Synth/OscilGen.cpp
@@ -665,7 +665,7 @@ void OscilGen::spectrumadjust(fft_t *freqs)
mag = 1.0f;
break;
}
- freqs[i] = std::polar<fftw_real>(mag, phase);
+ freqs[i] = FFTpolar<fftw_real>(mag, phase);
}
}
@@ -766,7 +766,7 @@ void OscilGen::prepare(fft_t *freqs)
int k = i * (j + 1);
if(k >= synth->oscilsize / 2)
break;
- freqs[k] += basefuncFFTfreqs[i] * std::polar<fftw_real>(
+ freqs[k] += basefuncFFTfreqs[i] * FFTpolar<fftw_real>(
hmag[j],
hphase[j] * k);
}
@@ -981,7 +981,7 @@ short int OscilGen::get(float *smps, float freqHz, int resonance)
const float rnd = PI * powf((Prand - 64.0f) / 64.0f, 2.0f);
for(int i = 1; i < nyquist - 1; ++i) //to Nyquist only for AntiAliasing
outoscilFFTfreqs[i] *=
- std::polar<fftw_real>(1.0f, (float)(rnd * i * RND));
+ FFTpolar<fftw_real>(1.0f, (float)(rnd * i * RND));
}
//Harmonic Amplitude Randomness
diff --git a/src/UI/Fl_EQGraph.cpp b/src/UI/Fl_EQGraph.cpp
@@ -3,6 +3,7 @@
#include "Fl_EQGraph.H"
#include "common.H"
#include "../Effects/EffectMgr.h"
+#include "../DSP/FFTwrapper.h"
#include "../globals.h"
#include <rtosc/rtosc.h>
@@ -139,8 +140,8 @@ double Fl_EQGraph::getresponse(int maxy,float freq) const
for(int i = 0; i < MAX_EQ_BANDS*MAX_FILTER_STAGES*2+1; ++i) {
- num_res += std::polar<float>(num[i], i*angle);
- dem_res += std::polar<float>(dem[i], i*angle);
+ num_res += FFTpolar<float>(num[i], i*angle);
+ dem_res += FFTpolar<float>(dem[i], i*angle);
}
float dbresp=20*log(abs(num_res/dem_res))/log(10);
diff --git a/src/UI/PartUI.fl b/src/UI/PartUI.fl
@@ -996,7 +996,6 @@ assert(!loc_.empty());
bankui=bankui_;
part_path = part_path_;
base = part_path;
-osc = osc;
npart=npart_;
ninseff=0;
osc=osc_;