From 7c875a14b73d844f4f3b8390e4463610262d9415 Mon Sep 17 00:00:00 2001
From: Florian Jung <florian.a.jung@web.de>
Date: Wed, 11 Apr 2012 16:36:45 +0200
Subject: added FM-list (brings between 0% and 15% performance improvement)
 cast-optimisation programs gets a better operator= commented out segfaulting
 cleanup()

---
 synth/Makefile      |  2 +-
 synth/OPTIMIZATIONS | 14 ++++++++++++++
 synth/avg_stddev.py | 27 +++++++++++++++++++++++++++
 synth/main.cpp      |  2 +-
 synth/note.cpp      | 41 ++++++++++++++++++++++++++++++++---------
 synth/note.h        |  3 +++
 synth/profile.sh    | 12 ++++++++++++
 synth/programs.cpp  | 20 ++++++++++++++++----
 8 files changed, 106 insertions(+), 15 deletions(-)
 create mode 100755 synth/avg_stddev.py
 create mode 100755 synth/profile.sh

(limited to 'synth')

diff --git a/synth/Makefile b/synth/Makefile
index 01e27ee..d97ec90 100644
--- a/synth/Makefile
+++ b/synth/Makefile
@@ -1,6 +1,6 @@
 CXX=g++
 #CFLAGS=-Wall -O2
-CFLAGS=-Wall -O -g
+CFLAGS=-Wall -O3 -g
 CXXFLAGS=$(CFLAGS)
 LDFLAGS=-lm `pkg-config --cflags --libs jack`
 
diff --git a/synth/OPTIMIZATIONS b/synth/OPTIMIZATIONS
index 14d5f4f..4a22fd4 100644
--- a/synth/OPTIMIZATIONS
+++ b/synth/OPTIMIZATIONS
@@ -2,6 +2,20 @@ Sinnlose Optimierungen
    o if(foo.fm_strength!=0) ...: kein effekt, höchstens leichter anstieg!
  
 Mögliche Optimierungen
+   o ??? if (out>0) und was mit lfo: vlt das if weglassen?
+   o  1% noten kriegen direkten pointer auf ihre waves; (custom_wave-check entfällt)
+   x 15% fm-liste (die nur die oscs mit fm[i]!=0 enthält) statt
+         in O(n²) alle durchzutesten: bei 2 oszillatoren kein effekt,
+         bei 9 oscs der form 1->2->3...->9 (also 9 mod-beziehungen)
+         17% verbesserung
+   o ??? Channel::get_sample() soll mehrere frames berechnen.
+         soll außerdem das /ONE*VOL_FACTOR gleich mit einrechnen
+         (kann einmalig gleich mit Channel::volume verrechnet werden)
+   o 10% BOTTLENECKs in note.cpp fixen. actual_freq und abhängigkeiten
+         nur dann berechnen, wenn sie sich auch ändert (set_freq, porta,
+         pitchbend, freq-envelope)
+   o ??? log_verbose sollte ggf. garnicht interpretiert werden?
+
    o 10% filter ganz auf fixed_t umstellen?
    o  5% envelope::get_level nur alle n frames arbeiten lassen, sonst cachen?
    o  2% bei LFOs: bei jedem LFO-update die werte für env-max, freqfactor
diff --git a/synth/avg_stddev.py b/synth/avg_stddev.py
new file mode 100755
index 0000000..421a1aa
--- /dev/null
+++ b/synth/avg_stddev.py
@@ -0,0 +1,27 @@
+#!/usr/bin/python
+
+import sys
+import math
+
+vals=[]
+sum=0
+cnt=0
+
+for line in sys.stdin:
+	num=float(line)
+	vals.append(num)
+	sum=sum+num
+	cnt=cnt+1
+
+avg=sum/cnt
+
+stddev=0
+for val in vals:
+	stddev=stddev+ (val-avg)**2
+
+stddev=stddev/cnt
+stddev=math.sqrt(stddev)
+
+
+print "avg is    " , avg
+print "stddev is " , stddev
diff --git a/synth/main.cpp b/synth/main.cpp
index 95a6a7b..5427b75 100644
--- a/synth/main.cpp
+++ b/synth/main.cpp
@@ -199,7 +199,7 @@ int main(int argc, char** argv)
 			
 		do_in_synth_cli();
 		
-		cleanup();
+		//cleanup();
 	}
 	catch(string err)
 	{
diff --git a/synth/note.cpp b/synth/note.cpp
index 54267a8..f468a79 100644
--- a/synth/note.cpp
+++ b/synth/note.cpp
@@ -50,6 +50,11 @@ Note::Note(int n, float v, program_t &prg, jack_nframes_t pf, fixed_t pb, int pr
 	copy(&prg.osc_settings[0],&prg.osc_settings[n_oscillators],oscillator);
 	copy(&prg.osc_settings[0],&prg.osc_settings[n_oscillators],orig.oscillator);
 	
+	fm_oscs=new list<int>[n_oscillators];
+	for (int i=0;i<n_oscillators;++i)
+		for (int j=0;j<n_oscillators;++j)
+			if (oscillator[i].fm_strength[j]!=0)
+				fm_oscs[i].push_back(j);
 	
 	//initalize oscillator.phase to multiples of their wave resolution
 	//this has the following effect: the actual phase, i.e. the index
@@ -156,8 +161,13 @@ void Note::apply_pfactor()
 		oscillator[i].output=orig.oscillator[i].output*pfactor.out[i] >>SCALE;
 		oscillator[i].freq_env_amount=orig.oscillator[i].freq_env_amount*pfactor.freq_env_amount[i] /ONE; //because it's a float
 		
+		fm_oscs[i].clear();
 		for (int j=0;j<n_oscillators;++j)
+		{
 			oscillator[i].fm_strength[j]=orig.oscillator[i].fm_strength[j]*pfactor.fm[i][j] >>SCALE;
+			if (oscillator[i].fm_strength[j]!=0)
+				fm_oscs[i].push_back(j);
+		}
 	}
 	filter_params.env_amount=orig.filter_params.env_amount*pfactor.filter_env /ONE;
 	filter_params.freqfactor_offset=orig.filter_params.freqfactor_offset*pfactor.filter_offset /ONE;
@@ -321,7 +331,7 @@ fixed_t Note::get_sample()
 	if (freq!=dest_freq)
 	{
 		// the div.by.zero if p_frames=0 is avoided because then the 
-		// if-condition below is always true
+		// if-condition below would always be true
 		if (portamento_t>=portamento_frames)
 			freq=dest_freq;
 		else //will only happen if p_t < p_frames -> p_frames is always > 0 -> div. ok
@@ -342,11 +352,16 @@ fixed_t Note::get_sample()
 	fixed_t fm=0;
 	fixed_t out=0;
 	
-	int i,j;
+	int i;
 	
 	if (sync_factor)
 	{
-		sync_phase+=(actual_freq*sync_factor/samp_rate) >> SCALE;
+		sync_phase+=(actual_freq*sync_factor/samp_rate) >> SCALE; // BOTTLENECK
+		// phase-increment depends on:
+		// - actual_freq (which depends on freq and pitchbend)
+		//       steadily updated while portamento-ing and whenever a pitchbend comes in
+		// - sync_factor: only updated manually
+		// - samp_rate: never changes
 		
 		if (sync_phase >= ONE)
 		{
@@ -382,9 +397,9 @@ fixed_t Note::get_sample()
 	{
 		fm=0;
 		
-		for (j=0;j<n_oscillators;++j)
-			if (oscillator[i].fm_strength[j]!=0) //osc_j affects osc_i (FM)
-				fm+=old_oscval[j]*oscillator[i].fm_strength[j];
+		//iterate through all modulating oscs
+		for (list<int>::iterator j=fm_oscs[i].begin(), end=fm_oscs[i].end(); j!=end; j++)		
+			fm+=old_oscval[*j]*oscillator[i].fm_strength[*j];
 
 		fm=fm>>SCALE;
 		
@@ -392,18 +407,26 @@ fixed_t Note::get_sample()
 		if (oscillator[i].vibrato_depth!=0)
 			oscillator[i].phase+=(  (curr_lfo[oscillator[i].vibrato_lfo][oscillator[i].vibrato_depth]*actual_freq >>SCALE)*oscillator[i].factor/samp_rate)>>SCALE;
 		else
-			oscillator[i].phase+=(actual_freq*oscillator[i].factor/samp_rate)>>SCALE;
+			oscillator[i].phase+=(actual_freq*oscillator[i].factor/samp_rate)>>SCALE; // BOTTLENECK
+			// phase-increment depends on:
+			// - actual_freq (which depends on freq and pitchbend)
+			//       steadily updated while portamento-ing and whenever a pitchbend comes in
+			// - the vibrato-lfo: needs update whenever this lfo is updated
+			// - factor (which depends on the freq envelope)
+			//       steadily updated every env_frames frames
+			// - samp_rate: never changes
 			
 		if (oscillator[i].custom_wave)
 		{
 			//sampler
 			custom_wave_t *cw=oscillator[i].custom_wave;
-			oscval[i]=cw->wave[ ((oscillator[i].phase  +  fm) * cw->samp_rate >>(2*SCALE)) % cw->wave_len ] * envval[i]  >> (SCALE);
+			oscval[i]=cw->wave[ ((unsigned int)((oscillator[i].phase  +  fm) * cw->samp_rate >>(2*SCALE))) % cw->wave_len ] * envval[i]  >> (SCALE);
 		}
 		else
 		{
 			//normal oscillator
-			oscval[i]=wave[oscillator[i].waveform][ ((oscillator[i].phase  +  fm) * WAVE_RES >>SCALE) % WAVE_RES ] * envval[i] >> (SCALE);
+			//optimisation: the unsigned int cast avoids a slow 64bit modulo calculation. ca. 8% speed gain. same above.
+			oscval[i]=wave[oscillator[i].waveform][ ((unsigned int)((oscillator[i].phase  +  fm) * WAVE_RES >>SCALE)) % WAVE_RES ] * envval[i] >> (SCALE);
 		}
 
 		if (oscillator[i].tremolo_depth!=0)
diff --git a/synth/note.h b/synth/note.h
index 7181f6d..379f2b1 100644
--- a/synth/note.h
+++ b/synth/note.h
@@ -3,6 +3,8 @@
 
 #include <jack/jack.h>
 
+#include <list>
+
 #include "programs.h"
 #include "envelope.h"
 #include "fixed.h"
@@ -43,6 +45,7 @@ class Note : public NoteSkel
 		fixed_t *old_oscval;
 		int n_oscillators;
 		oscillator_t *oscillator;
+		std::list<int>* fm_oscs;
 		
 		fixed_t sync_factor;
 		fixed_t sync_phase;
diff --git a/synth/profile.sh b/synth/profile.sh
new file mode 100755
index 0000000..921a5d4
--- /dev/null
+++ b/synth/profile.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+if [ x$1 = x ]; then
+	cnt=5
+else
+	cnt=$1
+fi
+
+for ((i=1;i<=$cnt;i++)); do
+	/usr/bin/time -p ./synth -p 0:analog.prog -i 60 -x 1000:1 -a -m 2>&1 | grep 'user ' | sed 's/user //';
+	#echo "$i / $cnt done" 1>&2
+done | python avg_stddev.py
diff --git a/synth/programs.cpp b/synth/programs.cpp
index 5dae4ed..63078c0 100644
--- a/synth/programs.cpp
+++ b/synth/programs.cpp
@@ -26,15 +26,27 @@ oscillator_t& oscillator_t::operator=(const oscillator_t &that)
 {
 	if (this!=&that)
 	{
+		this->output=that.output;
+		this->waveform=that.waveform;
+		this->factor=that.factor;
+		this->freq_env_amount=that.freq_env_amount;
+		this->freq_env=that.freq_env;
+		this->phase=that.phase;
+		this->tremolo_depth=that.tremolo_depth;
+		this->tremolo_lfo=that.tremolo_lfo;
+		this->vibrato_depth=that.vibrato_depth;
+		this->vibrato_lfo=that.vibrato_lfo;
+		this->custom_wave=that.custom_wave;
+		this->n_osc=that.n_osc;
+		this->ksl=that.ksl;
+		this->ksr=that.ksr;
+		this->sync=that.sync;
+
 		if (this->fm_strength)
 			delete [] this->fm_strength;
 
-		memcpy(this, &that, sizeof(*this));
-
 		this->fm_strength=new fixed_t[n_osc];
 		memcpy(this->fm_strength, that.fm_strength, sizeof(*that.fm_strength)*n_osc);
-		
-		this->custom_wave=that.custom_wave;
 
 		return *this;
 	}
-- 
cgit v1.2.3