zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

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:
Msrc/DSP/FFTwrapper.h | 19+++++++++++++++++++
Msrc/Effects/Distorsion.cpp | 4++--
Msrc/Effects/Reverb.cpp | 4++--
Msrc/Nio/OssEngine.cpp | 345+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/Nio/OssEngine.h | 27++++++++++++++++++++++-----
Msrc/Params/PADnoteParameters.cpp | 4++--
Msrc/Synth/OscilGen.cpp | 6+++---
Msrc/UI/Fl_EQGraph.cpp | 5+++--
Msrc/UI/PartUI.fl | 1-
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_;