📄 ath.c
字号:
/* $Id: ath.c,v 1.12 2000/12/05 15:37:26 aleidinger Exp $ *//* * Known bugs (sorted by importance): * - human delay (ca. 200 ms or more???) and buffering delay (341 ms @48 kHz/64 KByte) * should be subtracted * - error handling * - cos slope on direction changes * - calibration file of soundcard/amplifier/head phone * - worse handling * - +/- handling via mouse (do you have code?) in a dark room * - ENTER as direction change * - finer precalculated ATH for pre-emphasis *//* * Suggested level ranges: * 180 Hz...13.5 kHz: 50...70 dB * 100 Hz...15.0 kHz: 40...70 dB * 70 Hz...16.0 kHz: 30...70 dB * 45 Hz...16.5 kHz: 20...70 dB * 30 Hz...17.5 kHz: 10...70 dB * 25 Hz...18.0 kHz: 5...75 dB * 20 Hz...19.0 kHz: 0...80 dB * 16 Hz...20.0 kHz: -10...80 dB */#ifdef HAVE_CONFIG_H# include <config.h>#endif#include <assert.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>#include <limits.h>#include <termios.h>#include <math.h>#include <time.h>#include <signal.h>#include <sys/ioctl.h>#include <sys/time.h>#include <sys/types.h>#include <sys/stat.h>#ifdef HAVE_SYS_SOUNDCARD_H# include <sys/soundcard.h>#elif defined(HAVE_LINUX_SOUNDCARD_H)# include <linux/soundcard.h>#else# error no soundcard include#endif #define AUDIO_DEVICE "/dev/dsp"//#define COOLEDIT_FILE "/mnt/dosd/cooledit.wav"#define DELAY_UNTIL_XCHG 2.5#define TURN_STEPS 2400/****************************************************************************************************** * soundcard stuff ******************************************************************************************************/const double dither_coeff [] [16] = { { /* 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 }, { /* 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 }, { /* 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 }, { /* 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 }, { /* 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 }, { /* 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 }, { /* 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 },};typedef struct { const char* device; int fd; long double sample_freq; const double* dither; int channels; int bits;} soundcard_t;typedef signed short sample_t;typedef sample_t stereo_t [2]; int open_soundcard ( soundcard_t* const k, const char* device, const int channels, const int bits, const long double freq ){ int arg; int org; int index; int status; k->device = device; if ( -1 == (k->fd = open ( k->device, O_WRONLY )) ) { perror("opening of audio device failed"); return -1; } if ( -1 == (status = ioctl (k->fd, SOUND_PCM_SYNC, 0))) { fprintf ( stderr, "%s: SOUND_PCM_SYNC ioctl failed: %s\n", k->device, strerror (errno)); return -1; } org = arg = channels; if ( -1 == (status = ioctl (k->fd, SOUND_PCM_WRITE_CHANNELS, &arg)) ) { fprintf ( stderr, "%s: SOUND_PCM_WRITE_CHANNELS (%d) ioctl failed: %s\n" , k->device, channels, strerror (errno) ); return -1; } if (arg != org) { fprintf ( stderr, "%s: unable to set number of channels: %d instead of %d\n", k->device, arg, org ); return -1; } k->channels = arg; org = arg = bits; if ( -1 == (status = ioctl (k->fd, SOUND_PCM_WRITE_BITS, &arg)) ) { fprintf ( stderr, "%s: SOUND_PCM_WRITE_BITS ioctl failed\n", k->device ); return -1; } if (arg != org) { fprintf ( stderr, "%s: unable to set sample size: %d instead of %d\n", k->device, arg, org ); return -1; } k->bits = arg; org = arg = k->bits <= 8 ? AFMT_U8 : AFMT_S16_LE; if ( -1 == ioctl (k->fd, SNDCTL_DSP_SETFMT, &arg) ) { fprintf ( stderr, "%s: SNDCTL_DSP_SETFMT ioctl failed\n", k->device ); return -1; } if ((arg & org) == 0) { fprintf ( stderr, "%s: unable to set data format\n", k->device ); return -1; } org = arg = (int) floor ( freq + 0.5 ); if ( -1 == (status = ioctl (k->fd, SOUND_PCM_WRITE_RATE, &arg)) ) { fprintf ( stderr, "%s: SOUND_PCM_WRITE_WRITE ioctl failed\n", k->device ); return -1; } k->sample_freq = (long double)arg; index = (arg - 44000) / 8000; if ( index < 0 ) index = 0; if ( index >= sizeof(dither_coeff)/sizeof(*dither_coeff) ) index = sizeof(dither_coeff)/sizeof(*dither_coeff) - 1; k->dither = dither_coeff [ index ]; return 0;}int play_soundcard ( soundcard_t* const k, stereo_t* samples, size_t length ){ size_t bytes = length * sizeof (*samples); #ifdef COOLEDIT_FILE static int fd = -1; if ( fd < 0 ) fd = open ( COOLEDIT_FILE, O_WRONLY | O_CREAT ); write ( fd, samples, bytes );#endif return write ( k->fd, samples, bytes ) == bytes ? 0 : -1;}int close_soundcard ( soundcard_t* const k ){ return close (k->fd);}/****************************************************************************************************** * frequency stuff ******************************************************************************************************/typedef enum { linear = 0, logarithm = 1, square = 2, cubic = 3, erb = 4, recip = 5} genmode_t;static long double linear_f ( long double x ) { return x > 0.L ? x : 0.0L; }static long double logarithm_f ( long double x ) { return x > 0.L ? log10 (x) : -3.5L; }static long double square_f ( long double x ) { return x > 0.L ? sqrt (x) : 0.0L; }static long double cubic_f ( long double x ) { return x > 0.L ? pow (x,1/3.) : 0.0L; }static long double erb_f ( long double x ) { return log (1. + 0.00437*x); }static long double recip_f ( long double x ) { return x > 1.L ? 1.L/x : 1.0L; }static long double inv_linear_f ( long double x ) { return x; }static long double inv_logarithm_f ( long double x ) { return pow (10., x); }static long double inv_square_f ( long double x ) { return x*x; }static long double inv_cubic_f ( long double x ) { return x*x*x; }static long double inv_erb_f ( long double x ) { return (exp(x) - 1.) * (1./0.00437); }static long double inv_recip_f ( long double x ) { return x > 1.L ? 1.L/x : 1.0L; }typedef long double (*converter_fn_t) ( long double );const converter_fn_t func [] = { linear_f, logarithm_f, square_f, cubic_f , erb_f , recip_f };const converter_fn_t inv_func [] = { inv_linear_f, inv_logarithm_f, inv_square_f, inv_cubic_f, inv_erb_f, inv_recip_f };typedef struct { genmode_t genmode; long double start_freq; long double stop_freq; long double sample_freq; unsigned long duration; long double phase; long double param1; long double param2; unsigned long counter; } generator_t;int open_generator ( generator_t* const g, const soundcard_t* const s, const genmode_t genmode, const long double duration, const long double start_freq, const long double stop_freq ){ g->sample_freq = s->sample_freq; g->genmode = genmode; g->start_freq = start_freq; g->stop_freq = stop_freq; g->duration = (unsigned long) floor ( duration * g->sample_freq + 0.5 ); if ( g->duration < 2 ) return -1; if ( g->genmode >= sizeof (func)/sizeof(*func) ) return -1; g->param1 = func [g->genmode] ( g->start_freq / g->sample_freq ); g->param2 = ( func [ g->genmode ] ( g->stop_freq / g->sample_freq ) - g->param1 ) / ( g->duration - 1 ); g->phase = 0.L; g->counter= 0; return 0;}long double iterate_generator ( generator_t* const g ){ long double freq; freq = inv_func [ g->genmode ] ( g->param1 + g->counter++ * g->param2 ); g->phase += freq; if (g->phase > 15.) g->phase -= 16.; return sin ( 2.*M_PI * g->phase );}long double get_sine ( generator_t* const g ){ return sin ( 2.*M_PI * g->phase );}long double get_cosine ( generator_t* const g ){ return cos ( 2.*M_PI * g->phase );}long double frequency ( const generator_t* const g ){ return inv_func [ g->genmode ] ( g->param1 + g->counter * g->param2 ) * g->sample_freq;}int close_generator ( generator_t* const g ){ return 0;}/****************************************************************************************************** * amplitude stuff ******************************************************************************************************/typedef enum { up = 0, down = 1, turn_up = 2, turn_down = 3, still_up = 4, still_down = 5, change = 6} direction_t; typedef struct { long double sample_freq; direction_t direction; // down, up, still_up, still_down, turn_down, turn_up int multiplier; // -TURN_STEPS: down, +TURN_STEPS up long double amplitude; long double delta_amplitude; long direction_change;} amplitude_t;int open_amplifier ( amplitude_t* const a, const soundcard_t* const s, const long double start_ampl, const double dB_per_sec ){ a->sample_freq = s->sample_freq; a->direction = up; a->multiplier = +TURN_STEPS; a->amplitude = start_ampl * 32767.; a->delta_amplitude = dB_per_sec * 0.1151292546497022842 / s->sample_freq / TURN_STEPS; a->direction_change = 0; srand ( time (NULL) ); return 0;}long double iterate_amplifier ( amplitude_t* const a ){ switch ( a->direction ) { case still_up: assert (a->multiplier == +TURN_STEPS); if (a->direction_change > 0 ) a->direction_change--; else a->direction = turn_down; break; case still_down: assert (a->multiplier == -TURN_STEPS); if (a->direction_change > 0 ) a->direction_change--; else a->direction = turn_up; break; case turn_up: assert (a->direction_change == 0); if ( a->multiplier < +TURN_STEPS ) a->multiplier++; else a->direction = up; break; case turn_down: assert (a->direction_change == 0); if ( a->multiplier > -TURN_STEPS ) a->multiplier--; else a->direction = down; break; case up: assert (a->multiplier == +TURN_STEPS); assert (a->direction_change == 0); break; case down: assert (a->multiplier == -TURN_STEPS); assert (a->direction_change == 0); break; default: fprintf ( stderr, "\n\r*** Bug! ***\n"); break; } a->amplitude *= 1.L + a->delta_amplitude * a->multiplier; return a->amplitude; }long double amplitude ( const amplitude_t* const a ){ return a->amplitude / 32767.;}int change_direction ( amplitude_t* const a, direction_t new_direction ){ switch ( new_direction ) { case up: if (a->direction == down) { a->direction = still_down; } else { fprintf ( stderr, "Direction not down, so ignored\n" ); return -1; } break; case down: if (a->direction == up) { a->direction = still_up; } else { fprintf ( stderr, "Direction not up, so ignored\n" ); return -1; } break; case change: switch ( a->direction ) { case up: a->direction = still_up; break; case down: a->direction = still_down; break; default: fprintf ( stderr, "Direction still changing, so ignored\n" ); return -1; } break; default: fprintf ( stderr, "Direction unknown, so ignored\n" ); return -1; } a->direction_change = 1 + rand () * (a->sample_freq * DELAY_UNTIL_XCHG / RAND_MAX); return 0;}int close_amplifier ( amplitude_t* const a ){ return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -