/[svn]/linuxsampler/trunk/benchmarks/triang.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/benchmarks/triang.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3118 - (show annotations) (download)
Fri Apr 21 13:33:03 2017 UTC (4 weeks, 3 days ago) by schoenebeck
File size: 10732 byte(s)
* NKSP: Fixed crash when using built-in script array variable "%ALL_EVENTS".
* NKSP: Added built-in function "change_amp_lfo_depth()".
* NKSP: Added built-in function "change_amp_lfo_freq()".
* NKSP: Added built-in function "change_pitch_lfo_depth()".
* NKSP: Added built-in function "change_pitch_lfo_freq()".
* Bumped version (2.0.0.svn44).

1 /*
2 Triangle wave generator benchmark
3
4 This is a benchmark for comparison between a integer math, table lookup
5 and numeric sine wave harmonics solution.
6
7 Copyright (C) 2005 - 2017 Christian Schoenebeck <cuse@users.sf.net>
8 */
9
10 #include <math.h>
11 #include <time.h>
12 #include <stdio.h>
13
14 #include "../src/engines/common/LFOTriangleIntMath.h"
15 #include "../src/engines/common/LFOTriangleIntAbsMath.h"
16 #include "../src/engines/common/LFOTriangleDiHarmonic.h"
17
18 // whether we should not show any messages on the console
19 #ifndef SILENT
20 # define SILENT 0
21 #endif
22
23 // set to 1 if you want to output the three calculated waves as RAW files
24 // you can e.g. open it as RAW file in Rezound
25 // (32 bit SP-FP PCM, mono, little endian, 44100kHz)
26 #ifndef OUTPUT_AS_RAW_WAVE
27 # define OUTPUT_AS_RAW_WAVE 0
28 #endif
29
30 // how many sample points should we calculate in one sequence
31 #ifndef STEPS
32 # define STEPS 16384
33 #endif
34
35 // how often should we repeat the benchmark loop of each solution
36 #ifndef RUNS
37 # define RUNS 1000
38 #endif
39
40 // whether the wave should have positive and negative range (signed -> 1) or just positive (unsigned -> 0)
41 #ifndef SIGNED
42 # define SIGNED 1
43 #endif
44
45 // maximum value of the LFO output (also depends on SIGNED above)
46 #ifndef MAX
47 # define MAX 1.0f
48 #endif
49
50 // pro forma
51 #ifndef SAMPLING_RATE
52 # define SAMPLING_RATE 44100.0f
53 #endif
54
55 // return value of this benchmark
56 // to indicate the best performing solution
57 #define INT_MATH_SOLUTION 2 /* we don't start with 1, as this is reserved for unknown errors */
58 #define DI_HARMONIC_SOLUTION 3
59 #define TABLE_LOOKUP_SOLUTION 4 /* table lookup solution is currently disabled in this benchmark, see below */
60 #define INT_MATH_ABS_SOLUTION 5 /* integer math with abs() */
61 #define INVALID_RESULT -1
62
63 // we use 32 bit single precision floating point as sample point format
64 typedef float smpl_t; // (sample_t is already defined as int16_t by global_private.h)
65
66 using namespace LinuxSampler;
67
68 #if SIGNED
69 LFOTriangleIntMath<range_signed>* pIntLFO = NULL;
70 LFOTriangleIntAbsMath<range_signed>* pIntAbsLFO = NULL;
71 LFOTriangleDiHarmonic<range_signed>* pDiHarmonicLFO = NULL;
72 #else // unsigned
73 LFOTriangleIntMath<range_unsigned>* pIntLFO = NULL;
74 LFOTriangleIntAbsMath<range_unsigned>* pIntAbsLFO = NULL;
75 LFOTriangleDiHarmonic<range_unsigned>* pDiHarmonicLFO = NULL;
76 #endif
77
78 // integer math solution
79 float int_math(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
80 // pro forma
81 pIntLFO->trigger(frequency, start_level_max, 1200 /* max. internal depth */, 0, false, (unsigned int) SAMPLING_RATE);
82
83 clock_t stop_time;
84 clock_t start_time = clock();
85
86 for (int run = 0; run < RUNS; run++) {
87 pIntLFO->updateByMIDICtrlValue(127); // pro forma
88 for (int i = 0; i < steps; ++i) {
89 pDestinationBuffer[i] = pIntLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
90 }
91 }
92
93 stop_time = clock();
94 float elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
95 #if ! SILENT
96 printf("int math solution elapsed time: %1.0f ms\n", elapsed_time);
97 #endif
98
99 return elapsed_time;
100 }
101
102 // integer math abs solution
103 float int_math_abs(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
104 // pro forma
105 pIntAbsLFO->trigger(frequency, start_level_max, 1200 /* max. internal depth */, 0, false, (unsigned int) SAMPLING_RATE);
106
107 clock_t stop_time;
108 clock_t start_time = clock();
109
110 for (int run = 0; run < RUNS; run++) {
111 pIntAbsLFO->updateByMIDICtrlValue(0); // pro forma
112 for (int i = 0; i < steps; ++i) {
113 pDestinationBuffer[i] = pIntAbsLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
114 }
115 }
116
117 stop_time = clock();
118 float elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
119 #if ! SILENT
120 printf("int math abs solution elapsed time: %1.0f ms\n", elapsed_time);
121 #endif
122
123 return elapsed_time;
124 }
125
126 // table lookup solution (currently disabled)
127 //
128 // This solution is not yet implemented in LinuxSampler yet and probably
129 // never will, I simply haven't found an architectures / system where this
130 // turned out to be the best solution and it introduces too many problems
131 // anyway. If you found an architecture where this seems to be the best
132 // solution, please let us know!
133 #if 0
134 float table_lookup(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
135 // pro forma
136 const float r = frequency / SAMPLING_RATE; // frequency alteration quotient
137 #if SIGNED
138 float c = r * 4.0f;
139 #else
140 float c = r * 2.0f;
141 #endif
142 const int wl = (int) (SAMPLING_RATE / frequency); // wave length (in sample points)
143
144 // 'volatile' to avoid the cache to fudge the benchmark result
145 volatile float* pPrerenderingBuffer = new float[wl];
146
147 // prerendering of the triangular wave
148 {
149 float level = 0.0f;
150 for (int i = 0; i < wl; ++i) {
151 level += c;
152 #if SIGNED
153 if (level >= 1.0f) {
154 c = -c;
155 level = 1.0f;
156 }
157 else if (level <= -1.0f) {
158 c = -c;
159 level = -1.0f;
160 }
161 #else
162 if (level >= 1.0f) {
163 c = -c;
164 level = 1.0f;
165 }
166 else if (level <= 0.0f) {
167 c = -c;
168 level = 0.0f;
169 }
170 #endif
171 pPrerenderingBuffer[i] = level;
172 }
173 }
174
175 clock_t stop_time;
176 clock_t start_time = clock();
177
178 for (int run = 0; run < RUNS; run++) {
179 for (int i = 0; i < steps; ++i) {
180 pDestinationBuffer[i] = pPrerenderingBuffer[i % wl] * pAmp[i]; // * pAmp[i] just to simulate some memory load
181 }
182 }
183
184 stop_time = clock();
185 float elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
186 #if ! SILENT
187 printf("Table lookup solution elapsed time: %1.0f ms\n", elapsed_time);
188 #endif
189
190 if (pPrerenderingBuffer) delete[] pPrerenderingBuffer;
191
192 return elapsed_time;
193 }
194 #endif
195
196 // numeric, di-harmonic solution
197 float numeric_di_harmonic_solution(smpl_t* pDestinationBuffer, float* pAmp, const int steps, const float frequency) {
198 // pro forma
199 pDiHarmonicLFO->trigger(frequency, start_level_max, 1200 /* max. internal depth */, 0, false, (unsigned int) SAMPLING_RATE);
200
201 clock_t stop_time;
202 clock_t start_time = clock();
203
204 for (int run = 0; run < RUNS; run++) {
205 pDiHarmonicLFO->updateByMIDICtrlValue(127); // pro forma
206 for (int i = 0; i < steps; ++i) {
207 pDestinationBuffer[i] = pDiHarmonicLFO->render() * pAmp[i]; // * pAmp[i] just to simulate some memory load
208 }
209 }
210
211 stop_time = clock();
212 float elapsed_time = (stop_time - start_time) / (double(CLOCKS_PER_SEC) / 1000.0);
213 #if ! SILENT
214 printf("Numeric harmonics solution elapsed time: %1.0f ms\n", elapsed_time);
215 #endif
216
217 return elapsed_time;
218 }
219
220 // output calculated values as RAW audio format (32 bit floating point, mono) file
221 void output_as_raw_file(const char* filename, smpl_t* pOutputBuffer, int steps) {
222 FILE* file = fopen(filename, "w");
223 if (file) {
224 fwrite((void*) pOutputBuffer, sizeof(float), steps, file);
225 fclose(file);
226 } else {
227 fprintf(stderr, "Could not open %s\n", filename);
228 }
229 }
230
231 int main() {
232 const int steps = STEPS;
233 const int sinusoidFrequency = 100; // Hz
234
235 #if ! SILENT
236 # if SIGNED
237 printf("Signed triangular wave benchmark\n");
238 printf("--------------------------------\n");
239 # else
240 printf("Unsigned triangular wave benchmark\n");
241 printf("----------------------------------\n");
242 # endif
243 #endif
244
245 #if SIGNED
246 pIntLFO = new LFOTriangleIntMath<range_signed>(MAX);
247 pIntAbsLFO = new LFOTriangleIntAbsMath<range_signed>(MAX);
248 pDiHarmonicLFO = new LFOTriangleDiHarmonic<range_signed>(MAX);
249 #else // unsigned
250 pIntLFO = new LFOTriangleIntMath<range_unsigned>(MAX);
251 pIntAbsLFO = new LFOTriangleIntAbsMath<range_unsigned>(MAX);
252 pDiHarmonicLFO = new LFOTriangleDiHarmonic<range_unsigned>(MAX);
253 #endif
254
255 // output buffer for the calculated sinusoid wave
256 smpl_t* pOutputBuffer = new smpl_t[steps];
257 // just an arbitrary amplitude envelope to simulate a bit higher memory bandwidth
258 float* pAmplitude = new float[steps];
259
260 // pro forma - an arbitary amplitude envelope
261 for (int i = 0; i < steps; ++i)
262 pAmplitude[i] = (float) i / (float) steps;
263
264 // how long each solution took (in seconds)
265 float int_math_result, int_math_abs_result, /*table_lookup_result,*/ numeric_di_harmonic_result;
266
267 int_math_result = int_math(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
268 #if OUTPUT_AS_RAW_WAVE
269 output_as_raw_file("bench_int_math.raw", pOutputBuffer, steps);
270 #endif
271
272 int_math_abs_result = int_math_abs(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
273 #if OUTPUT_AS_RAW_WAVE
274 output_as_raw_file("bench_int_math_abs.raw", pOutputBuffer, steps);
275 #endif
276 //table_lookup_result = table_lookup(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
277 //#if OUTPUT_AS_RAW_WAVE
278 // output_as_raw_file("bench_table_lookup.raw", pOutputBuffer, steps);
279 //#endif
280 numeric_di_harmonic_result = numeric_di_harmonic_solution(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
281 #if OUTPUT_AS_RAW_WAVE
282 output_as_raw_file("bench_numeric_harmonics.raw", pOutputBuffer, steps);
283 #endif
284
285 #if ! SILENT
286 printf("\nOK, 2nd try\n\n");
287 #endif
288
289 int_math_result += int_math(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
290 int_math_abs_result = int_math_abs(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
291 //table_lookup_result += table_lookup(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
292 numeric_di_harmonic_result += numeric_di_harmonic_solution(pOutputBuffer, pAmplitude, steps, sinusoidFrequency);
293
294 if (pAmplitude) delete[] pAmplitude;
295 if (pOutputBuffer) delete[] pOutputBuffer;
296
297 if (pIntLFO) delete pIntLFO;
298 if (pDiHarmonicLFO) delete pDiHarmonicLFO;
299
300 if (int_math_abs_result <= int_math_result && int_math_abs_result <= numeric_di_harmonic_result) return INT_MATH_ABS_SOLUTION;
301 if (int_math_result <= int_math_abs_result && int_math_result <= numeric_di_harmonic_result) return INT_MATH_SOLUTION;
302 if (numeric_di_harmonic_result <= int_math_abs_result && numeric_di_harmonic_result <= int_math_result) return DI_HARMONIC_SOLUTION;
303
304 return INVALID_RESULT; // error
305 }

  ViewVC Help
Powered by ViewVC