1159b3361Sopenharmony_ci/* $Id$ */
2159b3361Sopenharmony_ci/*
3159b3361Sopenharmony_ci * Known bugs (sorted by importance):
4159b3361Sopenharmony_ci *     - human delay (ca. 200 ms or more???) and buffering delay (341 ms @48 kHz/64 KByte)
5159b3361Sopenharmony_ci *       should be subtracted
6159b3361Sopenharmony_ci *     - error handling
7159b3361Sopenharmony_ci *     - cos slope on direction changes
8159b3361Sopenharmony_ci *     - calibration file of soundcard/amplifier/head phone
9159b3361Sopenharmony_ci *     - worse handling
10159b3361Sopenharmony_ci *     - +/- handling via mouse (do you have code?) in a dark room
11159b3361Sopenharmony_ci *     - ENTER as direction change
12159b3361Sopenharmony_ci *     - finer precalculated ATH for pre-emphasis
13159b3361Sopenharmony_ci */
14159b3361Sopenharmony_ci
15159b3361Sopenharmony_ci/*
16159b3361Sopenharmony_ci * Suggested level ranges:
17159b3361Sopenharmony_ci *     180 Hz...13.5 kHz:  50...70 dB
18159b3361Sopenharmony_ci *     100 Hz...15.0 kHz:  40...70 dB
19159b3361Sopenharmony_ci *      70 Hz...16.0 kHz:  30...70 dB
20159b3361Sopenharmony_ci *      45 Hz...16.5 kHz:  20...70 dB
21159b3361Sopenharmony_ci *      30 Hz...17.5 kHz:  10...70 dB
22159b3361Sopenharmony_ci *      25 Hz...18.0 kHz:   5...75 dB
23159b3361Sopenharmony_ci *      20 Hz...19.0 kHz:   0...80 dB
24159b3361Sopenharmony_ci *      16 Hz...20.0 kHz: -10...80 dB
25159b3361Sopenharmony_ci */
26159b3361Sopenharmony_ci
27159b3361Sopenharmony_ci#ifdef HAVE_CONFIG_H
28159b3361Sopenharmony_ci# include <config.h>
29159b3361Sopenharmony_ci#endif
30159b3361Sopenharmony_ci
31159b3361Sopenharmony_ci#include <assert.h>
32159b3361Sopenharmony_ci#include <errno.h>
33159b3361Sopenharmony_ci#include <stdio.h>
34159b3361Sopenharmony_ci#include <stdlib.h>
35159b3361Sopenharmony_ci#include <unistd.h>
36159b3361Sopenharmony_ci#include <string.h>
37159b3361Sopenharmony_ci#include <fcntl.h>
38159b3361Sopenharmony_ci#include <limits.h>
39159b3361Sopenharmony_ci#include <termios.h>
40159b3361Sopenharmony_ci#include <math.h>
41159b3361Sopenharmony_ci#include <time.h>
42159b3361Sopenharmony_ci#include <signal.h>
43159b3361Sopenharmony_ci#include <sys/ioctl.h>
44159b3361Sopenharmony_ci#include <sys/time.h>
45159b3361Sopenharmony_ci#include <sys/types.h>
46159b3361Sopenharmony_ci#include <sys/stat.h>
47159b3361Sopenharmony_ci#ifdef HAVE_SYS_SOUNDCARD_H
48159b3361Sopenharmony_ci# include <sys/soundcard.h>
49159b3361Sopenharmony_ci#elif defined(HAVE_LINUX_SOUNDCARD_H)
50159b3361Sopenharmony_ci# include <linux/soundcard.h>
51159b3361Sopenharmony_ci#else
52159b3361Sopenharmony_ci# error no soundcard include
53159b3361Sopenharmony_ci#endif
54159b3361Sopenharmony_ci
55159b3361Sopenharmony_ci
56159b3361Sopenharmony_ci
57159b3361Sopenharmony_ci#define AUDIO_DEVICE       "/dev/dsp"
58159b3361Sopenharmony_ci//#define COOLEDIT_FILE      "/mnt/dosd/cooledit.wav"
59159b3361Sopenharmony_ci#define DELAY_UNTIL_XCHG   2.5
60159b3361Sopenharmony_ci#define TURN_STEPS	   2400
61159b3361Sopenharmony_ci
62159b3361Sopenharmony_ci/******************************************************************************************************
63159b3361Sopenharmony_ci *  soundcard stuff
64159b3361Sopenharmony_ci ******************************************************************************************************/
65159b3361Sopenharmony_ci
66159b3361Sopenharmony_ciconst double dither_coeff [] [16] = {
67159b3361Sopenharmony_ci    {  /* 48 kHz */ 3.35185352775391591311,  4.24914379295482032978,  1.78042251729150153086, -0.92601381419186201184, -1.37308596104182343645, -1.85951915999247704829, -3.28074437872632330526, -3.05496670185702990882, -1.22855462839450528837, -0.30291531959171267015, -0.18598486195652600770,  0.42010512205702003790,  0.92278786111368653452,  0.62102380451771775193,  0.14312897206650044828, -0.00454721508203927746 },
68159b3361Sopenharmony_ci    {  /* 56 kHz */ 3.86404134982280628749,  6.67195592701613291071,  5.90576195467245802046,  1.57589705921487261981, -2.10618201389737372178, -2.74191788822507184395, -2.62175070636849999396, -3.78505226463032808863, -4.45698848578010438284, -2.76825966243460536110, -0.26509931375584007312,  0.67853812028968716799,  0.17633528441477021892, -0.28511417191837823770, -0.21866605100975608470, -0.04751674094456833719 },
69159b3361Sopenharmony_ci    {  /* 64 kHz */ 4.09276938880098092172,  8.27424044674659812937, 10.11503162292146762880,  7.19159801569544317353,  1.39770070291739556523, -2.86595901981244688601, -3.76567274050094691362, -3.58051445684472378298, -4.78262917738758022539, -6.53075750894777650899, -6.31330514306857055627, -3.69971382767763534195, -0.78125094191744878298,  0.59027508113837267217,  0.53500264009607367648,  0.14860043567206217506 },
70159b3361Sopenharmony_ci    {  /* 72 kHz */ 4.13833553801985235465,  9.02461778089340082437, 12.93090366932740510782, 12.66372285767699051948,  7.76122176702274149630,  1.30617257555732278296, -2.92859120887121285358, -4.02438598495837830627, -4.16673068132491936262, -5.55618065300129916574, -7.82657788611231653103, -8.83055904466106668035, -7.34884789347713815672, -4.33977664906048314891, -1.67711310288611975398, -0.33086687044710235420 },
71159b3361Sopenharmony_ci    {  /* 80 kHz */ 4.22135293342667005517,  9.76639846582539722375, 15.46562682418357478290, 17.54378549927855248346, 13.29112084313158963396,  3.51512441998252657470, -7.51025671462502577300,-14.84164320864536219368,-16.10306907358826504148,-12.54775907691866414402, -7.40560667268782655149, -3.34708029482052565732, -1.19572214872925790860, -0.39582185216275086786, -0.14803160816846603424, -0.04292818488627011881 },
72159b3361Sopenharmony_ci    {  /* 88 kHz */ 4.18521467865996935325,  9.96765821475909556942, 16.91905760389390617551, 21.74016824668913557689, 20.96457146354060682367, 13.28640453421253890542,  0.85116933842171101587,-11.66054516261007127469,-19.62750656985581800169,-20.98831962473015904508,-16.95374072505042825458,-10.68848180295390154146, -5.17169792984369678908, -1.79975409439650319129, -0.38057073791415898674, -0.02672653932844656975 },
73159b3361Sopenharmony_ci    {  /* 96 kHz */ 4.09418877324899473189,  9.77977364010870211207, 17.10120082680385341159, 23.37356217615995036818, 25.27121942060722374276, 20.64059991613550174190,  9.99721445051475610371, -3.39833000550997938512,-15.03410054392933377278,-21.36704201000683067679,-21.40772859969388741685,-16.79355426136657673808,-10.48570200688141622163, -5.07642951516127438486, -1.75555240936989159436, -0.33817997298586054131 },
74159b3361Sopenharmony_ci};
75159b3361Sopenharmony_ci
76159b3361Sopenharmony_citypedef struct {
77159b3361Sopenharmony_ci    const char*    device;
78159b3361Sopenharmony_ci    int            fd;
79159b3361Sopenharmony_ci    long double    sample_freq;
80159b3361Sopenharmony_ci    const double*  dither;
81159b3361Sopenharmony_ci    int            channels;
82159b3361Sopenharmony_ci    int            bits;
83159b3361Sopenharmony_ci} soundcard_t;
84159b3361Sopenharmony_ci
85159b3361Sopenharmony_citypedef signed short  sample_t;
86159b3361Sopenharmony_citypedef sample_t      stereo_t [2];
87159b3361Sopenharmony_ci
88159b3361Sopenharmony_ciint  open_soundcard (
89159b3361Sopenharmony_ci    soundcard_t* const  k,
90159b3361Sopenharmony_ci    const char*         device,
91159b3361Sopenharmony_ci    const int           channels,
92159b3361Sopenharmony_ci    const int           bits,
93159b3361Sopenharmony_ci    const long double   freq )
94159b3361Sopenharmony_ci{
95159b3361Sopenharmony_ci    int  arg;
96159b3361Sopenharmony_ci    int  org;
97159b3361Sopenharmony_ci    int  index;
98159b3361Sopenharmony_ci    int  status;
99159b3361Sopenharmony_ci
100159b3361Sopenharmony_ci    k->device = device;
101159b3361Sopenharmony_ci    if ( -1 == (k->fd = open ( k->device, O_WRONLY )) ) {
102159b3361Sopenharmony_ci        perror("opening of audio device failed");
103159b3361Sopenharmony_ci	return -1;
104159b3361Sopenharmony_ci    }
105159b3361Sopenharmony_ci
106159b3361Sopenharmony_ci    if ( -1 == (status = ioctl (k->fd, SOUND_PCM_SYNC, 0))) {
107159b3361Sopenharmony_ci        fprintf ( stderr, "%s: SOUND_PCM_SYNC ioctl failed: %s\n", k->device, strerror (errno));
108159b3361Sopenharmony_ci        return -1;
109159b3361Sopenharmony_ci    }
110159b3361Sopenharmony_ci
111159b3361Sopenharmony_ci    org = arg = channels;
112159b3361Sopenharmony_ci    if ( -1 == (status = ioctl (k->fd, SOUND_PCM_WRITE_CHANNELS, &arg)) ) {
113159b3361Sopenharmony_ci	fprintf ( stderr, "%s: SOUND_PCM_WRITE_CHANNELS (%d) ioctl failed: %s\n" , k->device, channels, strerror (errno) );
114159b3361Sopenharmony_ci	return -1;
115159b3361Sopenharmony_ci    }
116159b3361Sopenharmony_ci    if (arg != org) {
117159b3361Sopenharmony_ci	fprintf ( stderr, "%s: unable to set number of channels: %d instead of %d\n", k->device, arg, org );
118159b3361Sopenharmony_ci	return -1;
119159b3361Sopenharmony_ci    }
120159b3361Sopenharmony_ci    k->channels = arg;
121159b3361Sopenharmony_ci
122159b3361Sopenharmony_ci    org = arg = bits;
123159b3361Sopenharmony_ci    if ( -1 == (status = ioctl (k->fd, SOUND_PCM_WRITE_BITS, &arg)) ) {
124159b3361Sopenharmony_ci	fprintf ( stderr, "%s: SOUND_PCM_WRITE_BITS ioctl failed\n", k->device );
125159b3361Sopenharmony_ci	return -1;
126159b3361Sopenharmony_ci    }
127159b3361Sopenharmony_ci    if (arg != org) {
128159b3361Sopenharmony_ci	fprintf ( stderr, "%s: unable to set sample size: %d instead of %d\n", k->device, arg, org );
129159b3361Sopenharmony_ci	return -1;
130159b3361Sopenharmony_ci    }
131159b3361Sopenharmony_ci    k->bits = arg;
132159b3361Sopenharmony_ci
133159b3361Sopenharmony_ci    org = arg = k->bits <= 8  ?  AFMT_U8  :  AFMT_S16_LE;
134159b3361Sopenharmony_ci    if ( -1 == ioctl (k->fd, SNDCTL_DSP_SETFMT, &arg) ) {
135159b3361Sopenharmony_ci	fprintf ( stderr, "%s: SNDCTL_DSP_SETFMT ioctl failed\n", k->device );
136159b3361Sopenharmony_ci	return -1;
137159b3361Sopenharmony_ci    }
138159b3361Sopenharmony_ci    if ((arg & org) == 0) {
139159b3361Sopenharmony_ci	fprintf ( stderr, "%s: unable to set data format\n", k->device );
140159b3361Sopenharmony_ci	return -1;
141159b3361Sopenharmony_ci    }
142159b3361Sopenharmony_ci
143159b3361Sopenharmony_ci    org = arg = (int) floor ( freq + 0.5 );
144159b3361Sopenharmony_ci    if ( -1 == (status = ioctl (k->fd, SOUND_PCM_WRITE_RATE, &arg)) ) {
145159b3361Sopenharmony_ci	fprintf ( stderr, "%s: SOUND_PCM_WRITE_WRITE ioctl failed\n", k->device );
146159b3361Sopenharmony_ci	return -1;
147159b3361Sopenharmony_ci    }
148159b3361Sopenharmony_ci    k->sample_freq = (long double)arg;
149159b3361Sopenharmony_ci    index          = (arg - 44000) / 8000;
150159b3361Sopenharmony_ci    if ( index <                                           0 ) index = 0;
151159b3361Sopenharmony_ci    if ( index >= sizeof(dither_coeff)/sizeof(*dither_coeff) ) index = sizeof(dither_coeff)/sizeof(*dither_coeff) - 1;
152159b3361Sopenharmony_ci    k->dither      = dither_coeff [ index ];
153159b3361Sopenharmony_ci    return 0;
154159b3361Sopenharmony_ci}
155159b3361Sopenharmony_ci
156159b3361Sopenharmony_ciint  play_soundcard    ( soundcard_t* const k, stereo_t* samples, size_t length )
157159b3361Sopenharmony_ci{
158159b3361Sopenharmony_ci    size_t  bytes = length * sizeof (*samples);
159159b3361Sopenharmony_ci
160159b3361Sopenharmony_ci#ifdef COOLEDIT_FILE
161159b3361Sopenharmony_ci    static int fd = -1;
162159b3361Sopenharmony_ci    if ( fd < 0 ) fd = open ( COOLEDIT_FILE, O_WRONLY | O_CREAT );
163159b3361Sopenharmony_ci    write ( fd, samples, bytes );
164159b3361Sopenharmony_ci#endif
165159b3361Sopenharmony_ci
166159b3361Sopenharmony_ci    return write ( k->fd, samples, bytes ) == bytes  ?  0  :  -1;
167159b3361Sopenharmony_ci}
168159b3361Sopenharmony_ci
169159b3361Sopenharmony_ciint  close_soundcard   ( soundcard_t* const k )
170159b3361Sopenharmony_ci{
171159b3361Sopenharmony_ci    return close (k->fd);
172159b3361Sopenharmony_ci}
173159b3361Sopenharmony_ci
174159b3361Sopenharmony_ci
175159b3361Sopenharmony_ci/******************************************************************************************************
176159b3361Sopenharmony_ci *  frequency stuff
177159b3361Sopenharmony_ci ******************************************************************************************************/
178159b3361Sopenharmony_ci
179159b3361Sopenharmony_citypedef enum {
180159b3361Sopenharmony_ci    linear    = 0,
181159b3361Sopenharmony_ci    logarithm = 1,
182159b3361Sopenharmony_ci    square    = 2,
183159b3361Sopenharmony_ci    cubic     = 3,
184159b3361Sopenharmony_ci    erb       = 4,
185159b3361Sopenharmony_ci    recip     = 5
186159b3361Sopenharmony_ci} genmode_t;
187159b3361Sopenharmony_ci
188159b3361Sopenharmony_cistatic long double linear_f        ( long double x ) { return x > 0.L  ?  x             :  0.0L; }
189159b3361Sopenharmony_cistatic long double logarithm_f     ( long double x ) { return x > 0.L  ?  log10 (x)     : -3.5L; }
190159b3361Sopenharmony_cistatic long double square_f        ( long double x ) { return x > 0.L  ?  sqrt (x)      :  0.0L; }
191159b3361Sopenharmony_cistatic long double cubic_f         ( long double x ) { return x > 0.L  ?  pow (x,1/3.)  :  0.0L; }
192159b3361Sopenharmony_cistatic long double erb_f           ( long double x ) { return log (1. + 0.00437*x); }
193159b3361Sopenharmony_cistatic long double recip_f         ( long double x ) { return x > 1.L  ?  1.L/x         :  1.0L; }
194159b3361Sopenharmony_ci
195159b3361Sopenharmony_cistatic long double inv_linear_f    ( long double x ) { return x;  }
196159b3361Sopenharmony_cistatic long double inv_logarithm_f ( long double x ) { return pow (10., x);  }
197159b3361Sopenharmony_cistatic long double inv_square_f    ( long double x ) { return x*x;  }
198159b3361Sopenharmony_cistatic long double inv_cubic_f     ( long double x ) { return x*x*x;  }
199159b3361Sopenharmony_cistatic long double inv_erb_f       ( long double x ) { return (exp(x) - 1.) * (1./0.00437); }
200159b3361Sopenharmony_cistatic long double inv_recip_f     ( long double x ) { return x > 1.L  ?  1.L/x         :  1.0L; }
201159b3361Sopenharmony_ci
202159b3361Sopenharmony_citypedef long double (*converter_fn_t) ( long double );
203159b3361Sopenharmony_ci
204159b3361Sopenharmony_ciconst converter_fn_t  func     [] = { linear_f,     logarithm_f,     square_f,     cubic_f    , erb_f    , recip_f     };
205159b3361Sopenharmony_ciconst converter_fn_t  inv_func [] = { inv_linear_f, inv_logarithm_f, inv_square_f, inv_cubic_f, inv_erb_f, inv_recip_f };
206159b3361Sopenharmony_ci
207159b3361Sopenharmony_citypedef struct {
208159b3361Sopenharmony_ci    genmode_t      genmode;
209159b3361Sopenharmony_ci    long double    start_freq;
210159b3361Sopenharmony_ci    long double    stop_freq;
211159b3361Sopenharmony_ci    long double    sample_freq;
212159b3361Sopenharmony_ci    unsigned long  duration;
213159b3361Sopenharmony_ci
214159b3361Sopenharmony_ci    long double    phase;
215159b3361Sopenharmony_ci    long double    param1;
216159b3361Sopenharmony_ci    long double    param2;
217159b3361Sopenharmony_ci    unsigned long  counter;
218159b3361Sopenharmony_ci} generator_t;
219159b3361Sopenharmony_ci
220159b3361Sopenharmony_ciint  open_generator (
221159b3361Sopenharmony_ci    generator_t* const  g,
222159b3361Sopenharmony_ci    const soundcard_t*  const s,
223159b3361Sopenharmony_ci    const genmode_t     genmode,
224159b3361Sopenharmony_ci    const long double   duration,
225159b3361Sopenharmony_ci    const long double   start_freq,
226159b3361Sopenharmony_ci    const long double   stop_freq )
227159b3361Sopenharmony_ci{
228159b3361Sopenharmony_ci    g->sample_freq = s->sample_freq;
229159b3361Sopenharmony_ci    g->genmode     = genmode;
230159b3361Sopenharmony_ci    g->start_freq  = start_freq;
231159b3361Sopenharmony_ci    g->stop_freq   = stop_freq;
232159b3361Sopenharmony_ci    g->duration    = (unsigned long) floor ( duration * g->sample_freq + 0.5 );
233159b3361Sopenharmony_ci
234159b3361Sopenharmony_ci    if ( g->duration < 2 )
235159b3361Sopenharmony_ci	return -1;
236159b3361Sopenharmony_ci
237159b3361Sopenharmony_ci    if ( g->genmode >= sizeof (func)/sizeof(*func) )
238159b3361Sopenharmony_ci	return -1;
239159b3361Sopenharmony_ci
240159b3361Sopenharmony_ci    g->param1 = func [g->genmode] ( g->start_freq / g->sample_freq );
241159b3361Sopenharmony_ci    g->param2 = ( func [ g->genmode ] ( g->stop_freq / g->sample_freq ) - g->param1 )
242159b3361Sopenharmony_ci	      / ( g->duration - 1 );
243159b3361Sopenharmony_ci    g->phase  = 0.L;
244159b3361Sopenharmony_ci    g->counter= 0;
245159b3361Sopenharmony_ci
246159b3361Sopenharmony_ci    return 0;
247159b3361Sopenharmony_ci}
248159b3361Sopenharmony_ci
249159b3361Sopenharmony_cilong double  iterate_generator ( generator_t* const g )
250159b3361Sopenharmony_ci{
251159b3361Sopenharmony_ci    long double  freq;
252159b3361Sopenharmony_ci
253159b3361Sopenharmony_ci    freq = inv_func [ g->genmode ] ( g->param1 + g->counter++ * g->param2 );
254159b3361Sopenharmony_ci
255159b3361Sopenharmony_ci    g->phase += freq;
256159b3361Sopenharmony_ci    if (g->phase > 15.)
257159b3361Sopenharmony_ci	g->phase -= 16.;
258159b3361Sopenharmony_ci    return sin ( 2.*M_PI * g->phase );
259159b3361Sopenharmony_ci}
260159b3361Sopenharmony_ci
261159b3361Sopenharmony_cilong double  get_sine ( generator_t* const g )
262159b3361Sopenharmony_ci{
263159b3361Sopenharmony_ci    return sin ( 2.*M_PI * g->phase );
264159b3361Sopenharmony_ci}
265159b3361Sopenharmony_ci
266159b3361Sopenharmony_cilong double  get_cosine ( generator_t* const g )
267159b3361Sopenharmony_ci{
268159b3361Sopenharmony_ci    return cos ( 2.*M_PI * g->phase );
269159b3361Sopenharmony_ci}
270159b3361Sopenharmony_ci
271159b3361Sopenharmony_ci
272159b3361Sopenharmony_cilong double  frequency ( const generator_t* const g )
273159b3361Sopenharmony_ci{
274159b3361Sopenharmony_ci    return inv_func [ g->genmode ] ( g->param1 + g->counter * g->param2 ) * g->sample_freq;
275159b3361Sopenharmony_ci}
276159b3361Sopenharmony_ci
277159b3361Sopenharmony_ciint  close_generator ( generator_t* const g )
278159b3361Sopenharmony_ci{
279159b3361Sopenharmony_ci    return 0;
280159b3361Sopenharmony_ci}
281159b3361Sopenharmony_ci
282159b3361Sopenharmony_ci/******************************************************************************************************
283159b3361Sopenharmony_ci *  amplitude stuff
284159b3361Sopenharmony_ci ******************************************************************************************************/
285159b3361Sopenharmony_ci
286159b3361Sopenharmony_citypedef enum {
287159b3361Sopenharmony_ci    up         = 0,
288159b3361Sopenharmony_ci    down       = 1,
289159b3361Sopenharmony_ci    turn_up    = 2,
290159b3361Sopenharmony_ci    turn_down  = 3,
291159b3361Sopenharmony_ci    still_up   = 4,
292159b3361Sopenharmony_ci    still_down = 5,
293159b3361Sopenharmony_ci    change     = 6
294159b3361Sopenharmony_ci} direction_t;
295159b3361Sopenharmony_ci
296159b3361Sopenharmony_ci
297159b3361Sopenharmony_citypedef struct {
298159b3361Sopenharmony_ci    long double    sample_freq;
299159b3361Sopenharmony_ci    direction_t    direction;            // down, up, still_up, still_down, turn_down, turn_up
300159b3361Sopenharmony_ci    int            multiplier;           // -TURN_STEPS: down, +TURN_STEPS up
301159b3361Sopenharmony_ci    long double    amplitude;
302159b3361Sopenharmony_ci    long double    delta_amplitude;
303159b3361Sopenharmony_ci    long           direction_change;
304159b3361Sopenharmony_ci} amplitude_t;
305159b3361Sopenharmony_ci
306159b3361Sopenharmony_ciint  open_amplifier (
307159b3361Sopenharmony_ci    amplitude_t* const        a,
308159b3361Sopenharmony_ci    const soundcard_t* const  s,
309159b3361Sopenharmony_ci    const long double         start_ampl,
310159b3361Sopenharmony_ci    const double              dB_per_sec )
311159b3361Sopenharmony_ci{
312159b3361Sopenharmony_ci    a->sample_freq      = s->sample_freq;
313159b3361Sopenharmony_ci    a->direction        = up;
314159b3361Sopenharmony_ci    a->multiplier       = +TURN_STEPS;
315159b3361Sopenharmony_ci    a->amplitude        = start_ampl * 32767.;
316159b3361Sopenharmony_ci    a->delta_amplitude  = dB_per_sec * 0.1151292546497022842 / s->sample_freq / TURN_STEPS;
317159b3361Sopenharmony_ci    a->direction_change = 0;
318159b3361Sopenharmony_ci
319159b3361Sopenharmony_ci    srand ( time (NULL) );
320159b3361Sopenharmony_ci    return 0;
321159b3361Sopenharmony_ci}
322159b3361Sopenharmony_ci
323159b3361Sopenharmony_cilong double iterate_amplifier ( amplitude_t* const a )
324159b3361Sopenharmony_ci{
325159b3361Sopenharmony_ci    switch ( a->direction ) {
326159b3361Sopenharmony_ci    case still_up:
327159b3361Sopenharmony_ci        assert (a->multiplier == +TURN_STEPS);
328159b3361Sopenharmony_ci        if (a->direction_change > 0 )
329159b3361Sopenharmony_ci	    a->direction_change--;
330159b3361Sopenharmony_ci	else
331159b3361Sopenharmony_ci	    a->direction = turn_down;
332159b3361Sopenharmony_ci	break;
333159b3361Sopenharmony_ci    case still_down:
334159b3361Sopenharmony_ci        assert (a->multiplier == -TURN_STEPS);
335159b3361Sopenharmony_ci        if (a->direction_change > 0 )
336159b3361Sopenharmony_ci	    a->direction_change--;
337159b3361Sopenharmony_ci	else
338159b3361Sopenharmony_ci	    a->direction = turn_up;
339159b3361Sopenharmony_ci	break;
340159b3361Sopenharmony_ci    case turn_up:
341159b3361Sopenharmony_ci        assert (a->direction_change == 0);
342159b3361Sopenharmony_ci	if ( a->multiplier < +TURN_STEPS )
343159b3361Sopenharmony_ci	    a->multiplier++;
344159b3361Sopenharmony_ci	else
345159b3361Sopenharmony_ci	    a->direction = up;
346159b3361Sopenharmony_ci	break;
347159b3361Sopenharmony_ci    case turn_down:
348159b3361Sopenharmony_ci        assert (a->direction_change == 0);
349159b3361Sopenharmony_ci	if ( a->multiplier > -TURN_STEPS )
350159b3361Sopenharmony_ci	    a->multiplier--;
351159b3361Sopenharmony_ci	else
352159b3361Sopenharmony_ci	    a->direction = down;
353159b3361Sopenharmony_ci	break;
354159b3361Sopenharmony_ci    case up:
355159b3361Sopenharmony_ci        assert (a->multiplier == +TURN_STEPS);
356159b3361Sopenharmony_ci        assert (a->direction_change == 0);
357159b3361Sopenharmony_ci	break;
358159b3361Sopenharmony_ci    case down:
359159b3361Sopenharmony_ci        assert (a->multiplier == -TURN_STEPS);
360159b3361Sopenharmony_ci        assert (a->direction_change == 0);
361159b3361Sopenharmony_ci	break;
362159b3361Sopenharmony_ci    default:
363159b3361Sopenharmony_ci	fprintf ( stderr, "\n\r*** Bug! ***\n");
364159b3361Sopenharmony_ci	break;
365159b3361Sopenharmony_ci    }
366159b3361Sopenharmony_ci
367159b3361Sopenharmony_ci    a->amplitude *= 1.L + a->delta_amplitude * a->multiplier;
368159b3361Sopenharmony_ci    return a->amplitude;
369159b3361Sopenharmony_ci}
370159b3361Sopenharmony_ci
371159b3361Sopenharmony_cilong double amplitude ( const amplitude_t* const a )
372159b3361Sopenharmony_ci{
373159b3361Sopenharmony_ci    return a->amplitude / 32767.;
374159b3361Sopenharmony_ci}
375159b3361Sopenharmony_ci
376159b3361Sopenharmony_ciint change_direction ( amplitude_t* const a, direction_t new_direction )
377159b3361Sopenharmony_ci{
378159b3361Sopenharmony_ci    switch ( new_direction ) {
379159b3361Sopenharmony_ci    case up:
380159b3361Sopenharmony_ci        if (a->direction == down) {
381159b3361Sopenharmony_ci	    a->direction = still_down;
382159b3361Sopenharmony_ci	} else {
383159b3361Sopenharmony_ci	    fprintf ( stderr, "Direction not down, so ignored\n" );
384159b3361Sopenharmony_ci	    return -1;
385159b3361Sopenharmony_ci	}
386159b3361Sopenharmony_ci	break;
387159b3361Sopenharmony_ci    case down:
388159b3361Sopenharmony_ci        if (a->direction == up) {
389159b3361Sopenharmony_ci	    a->direction = still_up;
390159b3361Sopenharmony_ci	} else {
391159b3361Sopenharmony_ci	    fprintf ( stderr, "Direction not up, so ignored\n" );
392159b3361Sopenharmony_ci	    return -1;
393159b3361Sopenharmony_ci	}
394159b3361Sopenharmony_ci	break;
395159b3361Sopenharmony_ci    case change:
396159b3361Sopenharmony_ci        switch ( a->direction ) {
397159b3361Sopenharmony_ci        case up:
398159b3361Sopenharmony_ci	    a->direction = still_up;
399159b3361Sopenharmony_ci	    break;
400159b3361Sopenharmony_ci	case down:
401159b3361Sopenharmony_ci	    a->direction = still_down;
402159b3361Sopenharmony_ci	    break;
403159b3361Sopenharmony_ci	default:
404159b3361Sopenharmony_ci	    fprintf ( stderr, "Direction still changing, so ignored\n" );
405159b3361Sopenharmony_ci	    return -1;
406159b3361Sopenharmony_ci	}
407159b3361Sopenharmony_ci	break;
408159b3361Sopenharmony_ci
409159b3361Sopenharmony_ci    default:
410159b3361Sopenharmony_ci	fprintf ( stderr, "Direction unknown, so ignored\n" );
411159b3361Sopenharmony_ci	return -1;
412159b3361Sopenharmony_ci    }
413159b3361Sopenharmony_ci
414159b3361Sopenharmony_ci    a->direction_change = 1 + rand () * (a->sample_freq * DELAY_UNTIL_XCHG / RAND_MAX);
415159b3361Sopenharmony_ci    return 0;
416159b3361Sopenharmony_ci}
417159b3361Sopenharmony_ci
418159b3361Sopenharmony_ciint  close_amplifier ( amplitude_t* const a )
419159b3361Sopenharmony_ci{
420159b3361Sopenharmony_ci    return 0;
421159b3361Sopenharmony_ci}
422159b3361Sopenharmony_ci
423159b3361Sopenharmony_ci
424159b3361Sopenharmony_cidouble ATH ( double freq )
425159b3361Sopenharmony_ci{
426159b3361Sopenharmony_ci    static float tab [] = {
427159b3361Sopenharmony_ci        /*    10.0 */  96.69, 96.69, 96.26, 95.12,
428159b3361Sopenharmony_ci        /*    12.6 */  93.53, 91.13, 88.82, 86.76,
429159b3361Sopenharmony_ci        /*    15.8 */  84.69, 82.43, 79.97, 77.48,
430159b3361Sopenharmony_ci        /*    20.0 */  74.92, 72.39, 70.00, 67.62,
431159b3361Sopenharmony_ci        /*    25.1 */  65.29, 63.02, 60.84, 59.00,
432159b3361Sopenharmony_ci        /*    31.6 */  57.17, 55.34, 53.51, 51.67,
433159b3361Sopenharmony_ci        /*    39.8 */  50.04, 48.12, 46.38, 44.66,
434159b3361Sopenharmony_ci        /*    50.1 */  43.10, 41.73, 40.50, 39.22,
435159b3361Sopenharmony_ci        /*    63.1 */  37.23, 35.77, 34.51, 32.81,
436159b3361Sopenharmony_ci        /*    79.4 */  31.32, 30.36, 29.02, 27.60,
437159b3361Sopenharmony_ci        /*   100.0 */  26.58, 25.91, 24.41, 23.01,
438159b3361Sopenharmony_ci        /*   125.9 */  22.12, 21.25, 20.18, 19.00,
439159b3361Sopenharmony_ci        /*   158.5 */  17.70, 16.82, 15.94, 15.12,
440159b3361Sopenharmony_ci        /*   199.5 */  14.30, 13.41, 12.60, 11.98,
441159b3361Sopenharmony_ci        /*   251.2 */  11.36, 10.57,  9.98,  9.43,
442159b3361Sopenharmony_ci        /*   316.2 */   8.87,  8.46,  7.44,  7.12,
443159b3361Sopenharmony_ci        /*   398.1 */   6.93,  6.68,  6.37,  6.06,
444159b3361Sopenharmony_ci        /*   501.2 */   5.80,  5.55,  5.29,  5.02,
445159b3361Sopenharmony_ci        /*   631.0 */   4.75,  4.48,  4.22,  3.98,
446159b3361Sopenharmony_ci        /*   794.3 */   3.75,  3.51,  3.27,  3.22,
447159b3361Sopenharmony_ci        /*  1000.0 */   3.12,  3.01,  2.91,  2.68,
448159b3361Sopenharmony_ci        /*  1258.9 */   2.46,  2.15,  1.82,  1.46,
449159b3361Sopenharmony_ci        /*  1584.9 */   1.07,  0.61,  0.13, -0.35,
450159b3361Sopenharmony_ci        /*  1995.3 */  -0.96, -1.56, -1.79, -2.35,
451159b3361Sopenharmony_ci        /*  2511.9 */  -2.95, -3.50, -4.01, -4.21,
452159b3361Sopenharmony_ci        /*  3162.3 */  -4.46, -4.99, -5.32, -5.35,
453159b3361Sopenharmony_ci        /*  3981.1 */  -5.13, -4.76, -4.31, -3.13,
454159b3361Sopenharmony_ci        /*  5011.9 */  -1.79,  0.08,  2.03,  4.03,
455159b3361Sopenharmony_ci        /*  6309.6 */   5.80,  7.36,  8.81, 10.22,
456159b3361Sopenharmony_ci        /*  7943.3 */  11.54, 12.51, 13.48, 14.21,
457159b3361Sopenharmony_ci        /* 10000.0 */  14.79, 13.99, 12.85, 11.93,
458159b3361Sopenharmony_ci        /* 12589.3 */  12.87, 15.19, 19.14, 23.69,
459159b3361Sopenharmony_ci        /* 15848.9 */  33.52, 48.65, 59.42, 61.77,
460159b3361Sopenharmony_ci        /* 19952.6 */  63.85, 66.04, 68.33, 70.09,
461159b3361Sopenharmony_ci        /* 25118.9 */  70.66, 71.27, 71.91, 72.60,
462159b3361Sopenharmony_ci    };
463159b3361Sopenharmony_ci    double    freq_log;
464159b3361Sopenharmony_ci    double    dB;
465159b3361Sopenharmony_ci    unsigned  index;
466159b3361Sopenharmony_ci
467159b3361Sopenharmony_ci    if ( freq <    10. ) freq =    10.;
468159b3361Sopenharmony_ci    if ( freq > 25000. ) freq = 25000.;
469159b3361Sopenharmony_ci
470159b3361Sopenharmony_ci    freq_log = 40. * log10 (0.1 * freq);   /* 4 steps per third, starting at 10 Hz */
471159b3361Sopenharmony_ci    index    = (unsigned) freq_log;
472159b3361Sopenharmony_ci    assert ( index < sizeof(tab)/sizeof(*tab) );
473159b3361Sopenharmony_ci    dB = tab [index] * (1 + index - freq_log) + tab [index+1] * (freq_log - index);
474159b3361Sopenharmony_ci    return pow ( 10., 0.05*dB );
475159b3361Sopenharmony_ci}
476159b3361Sopenharmony_ci
477159b3361Sopenharmony_ci/******************************************************************************************************
478159b3361Sopenharmony_ci *  keyboard stuff
479159b3361Sopenharmony_ci ******************************************************************************************************/
480159b3361Sopenharmony_ci
481159b3361Sopenharmony_citypedef struct {
482159b3361Sopenharmony_ci    int             init;
483159b3361Sopenharmony_ci    struct termios  stored_setting;
484159b3361Sopenharmony_ci    struct termios  current_setting;
485159b3361Sopenharmony_ci} keyboard_t;
486159b3361Sopenharmony_ci
487159b3361Sopenharmony_cistatic keyboard_t* __k;
488159b3361Sopenharmony_ci
489159b3361Sopenharmony_ci/* Restore term-settings to those saved when term_init was called */
490159b3361Sopenharmony_ci
491159b3361Sopenharmony_cistatic void  term_restore (void)
492159b3361Sopenharmony_ci{
493159b3361Sopenharmony_ci    tcsetattr ( 0, TCSANOW, &(__k->stored_setting) );
494159b3361Sopenharmony_ci}  /* term_restore */
495159b3361Sopenharmony_ci
496159b3361Sopenharmony_ci/* Clean up terminal; called on exit */
497159b3361Sopenharmony_ci
498159b3361Sopenharmony_cistatic void  term_exit ( int sig )
499159b3361Sopenharmony_ci{
500159b3361Sopenharmony_ci    term_restore ();
501159b3361Sopenharmony_ci}  /* term_exit */
502159b3361Sopenharmony_ci
503159b3361Sopenharmony_ci/* Will be called when ctrl-Z is pressed, this correctly handles the terminal */
504159b3361Sopenharmony_ci
505159b3361Sopenharmony_cistatic void  term_ctrl_z ( int sig )
506159b3361Sopenharmony_ci{
507159b3361Sopenharmony_ci    signal ( SIGTSTP, term_ctrl_z );
508159b3361Sopenharmony_ci    term_restore ();
509159b3361Sopenharmony_ci    kill ( getpid(), SIGSTOP );
510159b3361Sopenharmony_ci}  /* term_ctrl_z */
511159b3361Sopenharmony_ci
512159b3361Sopenharmony_ci/* Will be called when application is continued after having been stopped */
513159b3361Sopenharmony_ci
514159b3361Sopenharmony_cistatic void  term_cont ( int sig )
515159b3361Sopenharmony_ci{
516159b3361Sopenharmony_ci    signal ( SIGCONT, term_cont );
517159b3361Sopenharmony_ci    tcsetattr ( 0, TCSANOW, &(__k->current_setting) );
518159b3361Sopenharmony_ci} /* term_cont() */
519159b3361Sopenharmony_ci
520159b3361Sopenharmony_ciint  open_keyboard    ( keyboard_t* const k )
521159b3361Sopenharmony_ci{
522159b3361Sopenharmony_ci    __k = k;
523159b3361Sopenharmony_ci    tcgetattr ( 0, &(k->stored_setting) );
524159b3361Sopenharmony_ci    tcgetattr ( 0, &(k->current_setting) );
525159b3361Sopenharmony_ci
526159b3361Sopenharmony_ci    signal ( SIGINT,  term_exit   );       /* We _must_ clean up when we exit */
527159b3361Sopenharmony_ci    signal ( SIGQUIT, term_exit   );
528159b3361Sopenharmony_ci    signal ( SIGTSTP, term_ctrl_z );       /* Ctrl-Z must also be handled     */
529159b3361Sopenharmony_ci    signal ( SIGCONT, term_cont   );
530159b3361Sopenharmony_ci//  atexit ( term_exit );
531159b3361Sopenharmony_ci
532159b3361Sopenharmony_ci    /* One or more characters are sufficient to cause a read to return */
533159b3361Sopenharmony_ci    cfmakeraw ( &(k->current_setting) );
534159b3361Sopenharmony_ci    k->current_setting.c_oflag     |= ONLCR | OPOST;  /* enables NL => CRLF on output */
535159b3361Sopenharmony_ci
536159b3361Sopenharmony_ci    tcsetattr ( 0, TCSANOW, &(k->current_setting) );
537159b3361Sopenharmony_ci    return 0;
538159b3361Sopenharmony_ci}
539159b3361Sopenharmony_ci
540159b3361Sopenharmony_ciint  getchar_keyboard ( keyboard_t* const k )
541159b3361Sopenharmony_ci{
542159b3361Sopenharmony_ci    struct timeval  t;
543159b3361Sopenharmony_ci    fd_set          fd [1];
544159b3361Sopenharmony_ci    int             ret;
545159b3361Sopenharmony_ci    unsigned char   c;
546159b3361Sopenharmony_ci
547159b3361Sopenharmony_ci    FD_SET (0, fd);
548159b3361Sopenharmony_ci    t.tv_sec  = 0;
549159b3361Sopenharmony_ci    t.tv_usec = 0;
550159b3361Sopenharmony_ci
551159b3361Sopenharmony_ci    ret = select ( 1, fd, NULL, NULL, &t );
552159b3361Sopenharmony_ci
553159b3361Sopenharmony_ci    switch ( ret ) {
554159b3361Sopenharmony_ci    case  0:
555159b3361Sopenharmony_ci        return -1;
556159b3361Sopenharmony_ci    case  1:
557159b3361Sopenharmony_ci        ret = read (0, &c, 1);
558159b3361Sopenharmony_ci        return ret == 1  ?  c  :  -1;
559159b3361Sopenharmony_ci    default:
560159b3361Sopenharmony_ci        return -2;
561159b3361Sopenharmony_ci    }
562159b3361Sopenharmony_ci}
563159b3361Sopenharmony_ci
564159b3361Sopenharmony_ciint  close_keyboard   ( keyboard_t* const k )
565159b3361Sopenharmony_ci{
566159b3361Sopenharmony_ci    term_restore ();
567159b3361Sopenharmony_ci    return 0;
568159b3361Sopenharmony_ci}
569159b3361Sopenharmony_ci
570159b3361Sopenharmony_ci
571159b3361Sopenharmony_ci/******************************************************************************************************
572159b3361Sopenharmony_ci *  reporting stuff
573159b3361Sopenharmony_ci ******************************************************************************************************/
574159b3361Sopenharmony_ci
575159b3361Sopenharmony_ciint  report_open ( void )
576159b3361Sopenharmony_ci{
577159b3361Sopenharmony_ci    static char buff [32767];
578159b3361Sopenharmony_ci    fflush  ( stdout );
579159b3361Sopenharmony_ci    setvbuf ( stdout, buff, _IOFBF, sizeof(buff) );
580159b3361Sopenharmony_ci    return 0;
581159b3361Sopenharmony_ci}
582159b3361Sopenharmony_ci
583159b3361Sopenharmony_ciint  report ( const generator_t* const g, const amplitude_t* const a )
584159b3361Sopenharmony_ci{
585159b3361Sopenharmony_ci    static double  last_freq  = -1.;
586159b3361Sopenharmony_ci    static double  last_level = -1.;
587159b3361Sopenharmony_ci    double         freq;
588159b3361Sopenharmony_ci    double         level;
589159b3361Sopenharmony_ci
590159b3361Sopenharmony_ci    freq  = frequency (g);
591159b3361Sopenharmony_ci    level = 20. * log10 (amplitude (a) * ATH (freq) ) + 80.;
592159b3361Sopenharmony_ci
593159b3361Sopenharmony_ci    if ( last_freq >= 0 )
594159b3361Sopenharmony_ci        printf ( "%11.3f %8.2f\n", sqrt (freq*last_freq), 0.5 * (level+last_level) );
595159b3361Sopenharmony_ci    printf ( "# %9.3f %8.2f\n", freq, level );
596159b3361Sopenharmony_ci
597159b3361Sopenharmony_ci    fflush ( stdout );
598159b3361Sopenharmony_ci
599159b3361Sopenharmony_ci    last_freq  = freq;
600159b3361Sopenharmony_ci    last_level = level;
601159b3361Sopenharmony_ci    return 0;
602159b3361Sopenharmony_ci}
603159b3361Sopenharmony_ci
604159b3361Sopenharmony_ciint  report_close ( void )
605159b3361Sopenharmony_ci{
606159b3361Sopenharmony_ci    printf ( "%%%%\n\n" );
607159b3361Sopenharmony_ci    fflush  ( stdout );
608159b3361Sopenharmony_ci    close ( dup ( fileno(stdout) ) );
609159b3361Sopenharmony_ci    setvbuf ( stdout, NULL, _IONBF, 0 );
610159b3361Sopenharmony_ci    return 0;
611159b3361Sopenharmony_ci}
612159b3361Sopenharmony_ci
613159b3361Sopenharmony_ci
614159b3361Sopenharmony_ci/******************************************************************************************************
615159b3361Sopenharmony_ci *  main stuff
616159b3361Sopenharmony_ci ******************************************************************************************************/
617159b3361Sopenharmony_ci
618159b3361Sopenharmony_citypedef enum {
619159b3361Sopenharmony_ci    left     = 0,
620159b3361Sopenharmony_ci    right    = 1,
621159b3361Sopenharmony_ci    phase0   = 2,
622159b3361Sopenharmony_ci    both     = 2,
623159b3361Sopenharmony_ci    phase90  = 3,
624159b3361Sopenharmony_ci    phase180 = 4,
625159b3361Sopenharmony_ci    phasemod = 5
626159b3361Sopenharmony_ci} earmode_t;
627159b3361Sopenharmony_ci
628159b3361Sopenharmony_cistatic long double scalar ( const double* a, const double* b )
629159b3361Sopenharmony_ci{
630159b3361Sopenharmony_ci    return  a[ 0]*b[ 0] + a[ 1]*b[ 1] + a[ 2]*b[ 2] + a[ 3]*b[ 3]
631159b3361Sopenharmony_ci           +a[ 4]*b[ 4] + a[ 5]*b[ 5] + a[ 6]*b[ 6] + a[ 7]*b[ 7]
632159b3361Sopenharmony_ci           +a[ 8]*b[ 8] + a[ 9]*b[ 9] + a[10]*b[10] + a[11]*b[11]
633159b3361Sopenharmony_ci           +a[12]*b[12] + a[13]*b[13] + a[14]*b[14] + a[15]*b[15];
634159b3361Sopenharmony_ci}
635159b3361Sopenharmony_ci
636159b3361Sopenharmony_ciint experiment ( generator_t* const  g,
637159b3361Sopenharmony_ci		 amplitude_t* const  a,
638159b3361Sopenharmony_ci		 keyboard_t*  const  k,
639159b3361Sopenharmony_ci		 soundcard_t* const  s,
640159b3361Sopenharmony_ci		 earmode_t           earmode )
641159b3361Sopenharmony_ci{
642159b3361Sopenharmony_ci    long           i;
643159b3361Sopenharmony_ci    int            j;
644159b3361Sopenharmony_ci    stereo_t       samples [512];
645159b3361Sopenharmony_ci    static double  quant_errors [2] [16];
646159b3361Sopenharmony_ci    long double    val;
647159b3361Sopenharmony_ci    double         ampl;
648159b3361Sopenharmony_ci    long           ival;
649159b3361Sopenharmony_ci
650159b3361Sopenharmony_ci    fprintf ( stderr, "\r+++  up  +++" );
651159b3361Sopenharmony_ci    for ( i = 0; i < g->duration; i += sizeof(samples)/sizeof(*samples) ) {
652159b3361Sopenharmony_ci        fprintf ( stderr, "%3lu%%\b\b\b\b", i*100lu/g->duration );
653159b3361Sopenharmony_ci
654159b3361Sopenharmony_ci	for (j = 0; j < sizeof(samples)/sizeof(*samples); j++ ) {
655159b3361Sopenharmony_ci	    ampl = iterate_amplifier (a) * ATH (frequency (g));
656159b3361Sopenharmony_ci	    val  = ampl * iterate_generator (g);
657159b3361Sopenharmony_ci	    ival = (long) floor ( val + 0.5 + scalar (quant_errors[0], s->dither) );
658159b3361Sopenharmony_ci
659159b3361Sopenharmony_ci	    if ( ival != (sample_t) ival ) {
660159b3361Sopenharmony_ci		report (g, a);
661159b3361Sopenharmony_ci		fprintf ( stderr, "\rOverrun     \n\n" );
662159b3361Sopenharmony_ci		return -1;
663159b3361Sopenharmony_ci	    }
664159b3361Sopenharmony_ci	    memmove ( & quant_errors [0] [1], & quant_errors [0] [0],
665159b3361Sopenharmony_ci	              sizeof(quant_errors[0]) - sizeof(quant_errors[0][0]) );
666159b3361Sopenharmony_ci	    quant_errors [0] [0] = val - ival;
667159b3361Sopenharmony_ci	    switch ( earmode ) {
668159b3361Sopenharmony_ci	    case both:
669159b3361Sopenharmony_ci	        samples [j] [0] = samples [j] [1] = ival;
670159b3361Sopenharmony_ci		break;
671159b3361Sopenharmony_ci	    case left:
672159b3361Sopenharmony_ci	        samples [j] [0] = ival;
673159b3361Sopenharmony_ci		samples [j] [1] = 0;
674159b3361Sopenharmony_ci		break;
675159b3361Sopenharmony_ci	    case right:
676159b3361Sopenharmony_ci	        samples [j] [0] = 0;
677159b3361Sopenharmony_ci		samples [j] [1] = ival;
678159b3361Sopenharmony_ci		break;
679159b3361Sopenharmony_ci	    case phase180:
680159b3361Sopenharmony_ci	        samples [j] [0] = ival == -32768 ? 32767 : -ival;
681159b3361Sopenharmony_ci		samples [j] [1] = +ival;
682159b3361Sopenharmony_ci		break;
683159b3361Sopenharmony_ci	    case phase90:
684159b3361Sopenharmony_ci	        samples [j] [0] = ival;
685159b3361Sopenharmony_ci	        val  = ampl * get_cosine (g);
686159b3361Sopenharmony_ci	        ival = (long) floor ( val + 0.5 + scalar (quant_errors[1], s->dither) );
687159b3361Sopenharmony_ci	        if ( ival != (sample_t) ival ) {
688159b3361Sopenharmony_ci		    report (g, a);
689159b3361Sopenharmony_ci		    fprintf ( stderr, "\rOverrun     \n\n" );
690159b3361Sopenharmony_ci		    return -1;
691159b3361Sopenharmony_ci	        }
692159b3361Sopenharmony_ci	        memmove ( & quant_errors [1] [1], & quant_errors [1] [0],
693159b3361Sopenharmony_ci	              sizeof(quant_errors[1]) - sizeof(quant_errors[1][0]) );
694159b3361Sopenharmony_ci  	        quant_errors [1] [0] = val - ival;
695159b3361Sopenharmony_ci	        samples [j] [1] = ival;
696159b3361Sopenharmony_ci		break;
697159b3361Sopenharmony_ci	    default:
698159b3361Sopenharmony_ci	        assert (0);
699159b3361Sopenharmony_ci		return -1;
700159b3361Sopenharmony_ci	    }
701159b3361Sopenharmony_ci	}
702159b3361Sopenharmony_ci	play_soundcard ( s, samples, sizeof(samples)/sizeof(*samples) );
703159b3361Sopenharmony_ci	if ( amplitude (a) * ATH (frequency (g)) <= 3.16227766e-6 ) {
704159b3361Sopenharmony_ci            report (g, a);
705159b3361Sopenharmony_ci	    fprintf ( stderr, "\rUnderrun      \n\n" );
706159b3361Sopenharmony_ci	    return -1;
707159b3361Sopenharmony_ci	}
708159b3361Sopenharmony_ci
709159b3361Sopenharmony_ci	switch ( getchar_keyboard (k) ) {
710159b3361Sopenharmony_ci	case '+':
711159b3361Sopenharmony_ci	    fprintf ( stderr, "\r+++  up  +++" );
712159b3361Sopenharmony_ci	    report (g, a);
713159b3361Sopenharmony_ci	    change_direction ( a, up );
714159b3361Sopenharmony_ci	    break;
715159b3361Sopenharmony_ci	case '-':
716159b3361Sopenharmony_ci	    fprintf ( stderr, "\r--- down ---" );
717159b3361Sopenharmony_ci            report (g, a);
718159b3361Sopenharmony_ci	    change_direction ( a, down );
719159b3361Sopenharmony_ci	    break;
720159b3361Sopenharmony_ci	case '\r':
721159b3361Sopenharmony_ci	case '\n':
722159b3361Sopenharmony_ci	    fprintf ( stderr, "\r** change **" );
723159b3361Sopenharmony_ci            report (g, a);
724159b3361Sopenharmony_ci	    change_direction ( a, change );
725159b3361Sopenharmony_ci	    break;
726159b3361Sopenharmony_ci	case 'C'&0x1F:
727159b3361Sopenharmony_ci	case 'q':
728159b3361Sopenharmony_ci	case 'Q':
729159b3361Sopenharmony_ci	case 'x':
730159b3361Sopenharmony_ci	case 'X':
731159b3361Sopenharmony_ci	    fprintf ( stderr, "\rBreak       \n\n" );
732159b3361Sopenharmony_ci	    fflush  ( stderr );
733159b3361Sopenharmony_ci	    return -1;
734159b3361Sopenharmony_ci	default:
735159b3361Sopenharmony_ci	    fprintf ( stderr, "\a" );
736159b3361Sopenharmony_ci	    break;
737159b3361Sopenharmony_ci	case -1:
738159b3361Sopenharmony_ci	    break;
739159b3361Sopenharmony_ci	}
740159b3361Sopenharmony_ci    }
741159b3361Sopenharmony_ci
742159b3361Sopenharmony_ci    fprintf ( stderr, "\rReady       \n\n" );
743159b3361Sopenharmony_ci    return 0;
744159b3361Sopenharmony_ci}
745159b3361Sopenharmony_ci
746159b3361Sopenharmony_cistatic void usage ( void )
747159b3361Sopenharmony_ci{
748159b3361Sopenharmony_ci    static const char help[] =
749159b3361Sopenharmony_ci        "'Absolute Threshold of Hearing' -- Version 0.07   (C) Frank Klemm 2000\n"
750159b3361Sopenharmony_ci	"\n"
751159b3361Sopenharmony_ci	"usage:\n"
752159b3361Sopenharmony_ci	"    ath  type minfreq maxfreq duration ampl_speed [start_level [earmode] > reportfile\n"
753159b3361Sopenharmony_ci	"\n"
754159b3361Sopenharmony_ci	"         type:         linear, logarithm, square, cubic, erb, recip\n"
755159b3361Sopenharmony_ci	"         minfreq:      initial frequency [Hz]\n"
756159b3361Sopenharmony_ci	"         maxfreq:      end frequency [Hz]\n"
757159b3361Sopenharmony_ci	"         duration:     duration of the experiment [s]\n"
758159b3361Sopenharmony_ci	"         ampl_speed:   amplitude slope speed [phon/s]\n"
759159b3361Sopenharmony_ci	"         start_level:  absolute level at startup [0...1]\n"
760159b3361Sopenharmony_ci	"         earmode:      left, right, both, phase90, phase180\n"
761159b3361Sopenharmony_ci	"\n"
762159b3361Sopenharmony_ci	"example:\n"
763159b3361Sopenharmony_ci        "    ath  erb  700 22000 600 3 0.0001 > result1\n"
764159b3361Sopenharmony_ci	"    ath  erb 1400    16 360 3 0.0001 > result2\n"
765159b3361Sopenharmony_ci	"\n"
766159b3361Sopenharmony_ci	"handling:\n"
767159b3361Sopenharmony_ci	"    press '-' once when you start hearing a tone\n"
768159b3361Sopenharmony_ci	"    press '+' once when you stop hearing a tone\n"
769159b3361Sopenharmony_ci	"    press 'q' to early leave the program\n"
770159b3361Sopenharmony_ci	"    on errors the pressed key is ignored\n";
771159b3361Sopenharmony_ci
772159b3361Sopenharmony_ci    fprintf ( stderr, "%s\n", help );
773159b3361Sopenharmony_ci}
774159b3361Sopenharmony_ci
775159b3361Sopenharmony_ciint main ( int argc, char** argv )
776159b3361Sopenharmony_ci{
777159b3361Sopenharmony_ci    generator_t  g;
778159b3361Sopenharmony_ci    amplitude_t  a;
779159b3361Sopenharmony_ci    soundcard_t  s;
780159b3361Sopenharmony_ci    keyboard_t   k;
781159b3361Sopenharmony_ci    genmode_t    genmode;
782159b3361Sopenharmony_ci    earmode_t    earmode;
783159b3361Sopenharmony_ci
784159b3361Sopenharmony_ci    if ( argc == 1 ) {
785159b3361Sopenharmony_ci        usage ();
786159b3361Sopenharmony_ci        system ( "./ath erb  700 22000 600 3 0.0001 > result1" );
787159b3361Sopenharmony_ci	system ( "./ath erb 1400    16 360 3 0.0001 > result2" );
788159b3361Sopenharmony_ci	system ( "xmgr result1 result2 &> /dev/null &" );
789159b3361Sopenharmony_ci	return 0;
790159b3361Sopenharmony_ci    }
791159b3361Sopenharmony_ci
792159b3361Sopenharmony_ci    if ( argc < 6 ) {
793159b3361Sopenharmony_ci	usage ();
794159b3361Sopenharmony_ci	return 1;
795159b3361Sopenharmony_ci    }
796159b3361Sopenharmony_ci
797159b3361Sopenharmony_ci    if      ( 0 == strncmp ( argv[1], "li" , 2) ) genmode = linear;
798159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[1], "lo" , 2) ) genmode = logarithm;
799159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[1], "sq" , 2) ) genmode = square;
800159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[1], "cu" , 2) ) genmode = cubic;
801159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[1], "er" , 2) ) genmode = erb;
802159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[1], "re" , 2) ) genmode = recip;
803159b3361Sopenharmony_ci    else {
804159b3361Sopenharmony_ci	usage ();
805159b3361Sopenharmony_ci	return 1;
806159b3361Sopenharmony_ci    }
807159b3361Sopenharmony_ci
808159b3361Sopenharmony_ci    if      ( argc < 8 )                              earmode = both;
809159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[7], "le"     , 2) ) earmode = left;
810159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[7], "ri"     , 2) ) earmode = right;
811159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[7], "bo"     , 2) ) earmode = both;
812159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[7], "phase9" , 6) ) earmode = phase90;
813159b3361Sopenharmony_ci    else if ( 0 == strncmp ( argv[7], "phase1" , 6) ) earmode = phase180;
814159b3361Sopenharmony_ci    else {
815159b3361Sopenharmony_ci	usage ();
816159b3361Sopenharmony_ci	return 1;
817159b3361Sopenharmony_ci    }
818159b3361Sopenharmony_ci
819159b3361Sopenharmony_ci
820159b3361Sopenharmony_ci    open_soundcard ( &s, AUDIO_DEVICE, sizeof(stereo_t)/sizeof(sample_t), CHAR_BIT*sizeof(sample_t), 96000.0 );
821159b3361Sopenharmony_ci    open_generator ( &g, &s, genmode, atof (argv[4]), atof (argv[2]), atof (argv[3]) );
822159b3361Sopenharmony_ci    open_amplifier ( &a, &s, argc > 6  ?  atof (argv[6])  :  0.0001, atof (argv[5]) );
823159b3361Sopenharmony_ci    open_keyboard  ( &k );
824159b3361Sopenharmony_ci
825159b3361Sopenharmony_ci    report_open    ( );
826159b3361Sopenharmony_ci    experiment     ( &g, &a, &k, &s, earmode );
827159b3361Sopenharmony_ci    report_close   ( );
828159b3361Sopenharmony_ci
829159b3361Sopenharmony_ci    close_keyboard ( &k );
830159b3361Sopenharmony_ci    close_amplifier( &a );
831159b3361Sopenharmony_ci    close_generator( &g );
832159b3361Sopenharmony_ci    close_soundcard( &s );
833159b3361Sopenharmony_ci
834159b3361Sopenharmony_ci    return 0;
835159b3361Sopenharmony_ci}
836159b3361Sopenharmony_ci
837159b3361Sopenharmony_ci/* end of ath.c */
838159b3361Sopenharmony_ci
839159b3361Sopenharmony_ci
840