#include "mrilib.h" static byte mulaw( float x ) ; /* prototype for mu-law conversion */ #undef DEFAULT_SRATE #define DEFAULT_SRATE 16000 /* get specific sound recordings */ #include "cs_sounds.h" /* stored in Audio subdirectory */ /*--------------------------------------------------------------------------*/ /*---------- find a sound playing program ----------*/ static int found_pprog = -1 ; static char *pprog = NULL ; static char *pprog_name = NULL ; static const int DEBUG = 0 ; char * get_sound_player(void) { if( found_pprog < 0 ){ char *eee ; eee = getenv("AFNI_SOUND_PLAYER") ; if( eee != NULL || THD_is_executable(eee) ){ pprog = strdup(eee) ; } else { pprog = THD_find_executable("play") ; /* sox */ if( pprog == NULL ) pprog = THD_find_executable("afplay") ; /* OS X */ if( pprog == NULL ) pprog = THD_find_executable("mplayer") ; /* Linux or Mac */ if( pprog == NULL ) pprog = THD_find_executable("aplay") ; /* Linux */ } found_pprog = (pprog != NULL) ; if( found_pprog ) pprog_name = THD_trailname(pprog,0) ; if( DEBUG ) INFO_message("get_sound_player: %s",(found_pprog)?pprog:"NULL") ; } return pprog ; } /*--------------------------------------------------------------------------*/ static char note_type[32] = "pluck" ; static int gain_value = -27 ; static int two_tone = 0 ; void set_sound_note_type( char *typ ) { if( typ == NULL || typ[0] == '\0' ){ strcpy(note_type,"pluck"); return; } if( strncmp(typ,"sine",3) == 0 || strncmp(typ,"square",3) == 0 || strncmp(typ,"triangle",3) == 0 || strncmp(typ,"sawtooth",3) == 0 || strncmp(typ,"trapezium",3) == 0 || strncmp(typ,"pluck",3) == 0 ){ strcpy( note_type , typ ) ; } else { WARNING_message("unknown sox note type '%s'",typ) ; } return ; } /*-----*/ void set_sound_gain_value( int ggg ) { if( ggg < 0 ) gain_value = ggg ; return ; } /*-----*/ void set_sound_twotone( int ggg ) { two_tone = ggg ; return ; } /*-----*/ static int psk_set = 0 ; /*-----*/ #if 0 #define NFORKLIST 4 static int nfork_pg = 0 ; static int forklist_pg[NFORKLIST] ; void play_sound_killpg(void){ int ii ; for( ii=0 ; ii < NFORKLIST ; ii++ ){ if( forklist_pg[ii] > 0 ){ killpg(forklist_pg[ii],SIGKILL) ; } } return ; } #endif /*-----*/ #if 0 static int nfork_id = 0 ; static int forklist_id[NFORKLIST] ; void play_sound_killid(void) { int ii ; INFO_message("play_sound_killid()") ; for( ii=0 ; ii < NFORKLIST ; ii++ ){ if( forklist_id[ii] > 0 ){ ININFO_message("try to kill pid %d",forklist_id[ii]) ; kill(forklist_id[ii],SIGKILL) ; forklist_id[ii] = 0 ; } } nfork_id = 0 ; return ; } #endif /*-----*/ void kill_sound_players(void) { char cmd[1024] ; if( pprog_name != NULL ){ sprintf(cmd,"tcsh -c 'killall %s >& /dev/null'",pprog_name) ; system(cmd) ; } return ; } /*---------------------------------------------------------------------------*/ static char *play_append = NULL ; void mri_sound_play_append( char *app ) /* 09 Aug 2019 */ { if( play_append != NULL ){ free(play_append) ; play_append = NULL ; } if( app != NULL && *app != '\0' ) play_append = strdup(app) ; return ; } static int play_notify = 0 ; void mri_play_sound_notify(int nn){ play_notify = nn ; return ; } static float play_rate_fac = 1.0f ; void mri_play_sound_rate_fac( float fff ){ if( fff > 0.1f && fff <= 10.0f ) play_rate_fac = fff ; } /*---------------------------------------------------------------------------*/ /* Play sound from a 1D image (up to 4 columns) */ void mri_play_sound( MRI_IMAGE *imin , int ignore ) { MRI_IMAGE *qim ; char *pre , fname[256] , cmd[2048] , extras[1024] ; int ii , nsper ; float dt ; (void)get_sound_player() ; if( imin == NULL || imin->nx < 2 || pprog == NULL ) return ; kill_sound_players() ; if( !psk_set ){ atexit(kill_sound_players) ; psk_set = 1 ; } ii = (int)fork() ; if( ii != 0 ){ /*-- return to parent process --*/ #if 0 int jj = getpgid(ii) ; if( nfork_pg == 0 || (nfork_pg > 0 && jj != forklist_pg[nfork_pg-1]) ){ forklist_pg[nfork_pg] = getpgid(ii) ; nfork_pg = (nfork_pg+1)%NFORKLIST ; } #endif #if 0 jj = ii ; if( nfork_id == 0 || (nfork_id > 0 && jj != forklist_id[nfork_id-1]) ){ forklist_id[nfork_id] = jj ; nfork_id = (nfork_id+1)%NFORKLIST ; } #endif return ; } dt = (imin->nx <= 100) ? 0.20f : 0.14f + 6.0f/(float)imin->nx ; dt *= play_rate_fac ; nsper = (int)rintf(DEFAULT_SRATE*dt) ; qim = mri_sound_1D_to_notes( imin , DEFAULT_SRATE , nsper , 4 , ignore , 0 ) ; if( qim == NULL ) _exit(0) ; pre = UNIQ_idcode_11() ; /* make up name for sound file */ sprintf(fname,"AFNI_SOUND_TEMP.%s.au",pre) ; unlink(fname) ; /* remove sound file, in case it already exists */ if( DEBUG ) ININFO_message(" temp sound filename = %s",fname) ; if( play_notify ) ININFO_message(" writing temp sound file: %s",fname) ; sound_write_au_16PCM( fname, qim->nx, MRI_FLOAT_PTR(qim), DEFAULT_SRATE, 0.1f ) ; extras[0] = '\0' ; if( strcmp(pprog_name,"play") == 0 ){ strcat(extras," reverb 44 ") ; if( play_append != NULL ) strcat(extras,play_append) ; } sprintf(cmd,"tcsh -c '%s %s %s >& /dev/null'",pprog,fname,extras) ; if( DEBUG ) ININFO_message("%s",cmd) ; if( play_notify ) ININFO_message(" starting sound player: %s",cmd) ; system(cmd) ; sleep(1); unlink(fname) ; _exit(0) ; } /*---------------------------------------------------------------------------*/ /*-------- convert value in range -1..1 into 8 bit code via 'mu-law' --------*/ static INLINE byte mulaw( float x ) { short sx ; byte bx ; int sr , im ; short is , imag, iesp, ofst, ofst1 , sp ; if( x < -1.0f ) x = -1.0f ; else if( x > 1.0f ) x = 1.0f ; sx = (short)rintf(8158.0f * x) ; is = sx >> 15 ; sr = (sx & 65535) ; im = (is==0) ? sr : (65536-sr)&32767 ; imag = im ; if( imag > 8158 ) imag = 8158 ; ++imag; iesp = 0; ofst = 31; if( imag > ofst ){ for( iesp = 1 ; iesp <= 8 ; ++iesp){ ofst1 = ofst ; ofst += (1 << (iesp + 5)) ; if (imag <= ofst) break ; } imag -= ofst1 + 1 ; } imag /= (1 << (iesp + 1)) ; sp = (is == 0) ? (imag + (iesp << 4)) : (imag + (iesp << 4) + 128) ; sp ^= 128 ; sp ^= 127 ; bx = (byte)sp ; return bx ; } /*---------------------------------------------------------------------------*/ static int little_endian = -66 ; static INLINE void set_little_endian(void) { union { unsigned char bb[2] ; short ss ; } fred ; if( little_endian < 0 ){ fred.bb[0] = 1 ; fred.bb[1] = 0 ; little_endian = (fred.ss == 1) ; } return ; } /*---------------------------------------------------------------------------*/ typedef struct { unsigned char a,b,c,d ; } fourby ; static uint32_t INLINE swap_fourby( uint32_t ii ) { union { uint32_t qq ; fourby ff ; } qf ; unsigned char tt ; qf.qq = ii ; tt = qf.ff.a ; qf.ff.a = qf.ff.d ; qf.ff.d = tt ; tt = qf.ff.b ; qf.ff.b = qf.ff.c ; qf.ff.c = tt ; return qf.qq ; } typedef struct { unsigned char a,b ; } twoby ; static void swap_twobyar( int n , void *ar ) { int ii ; twoby *tb = (twoby *)ar ; unsigned char tt ; for( ii=0 ; ii < n ; ii++ ){ tt = tb[ii].a ; tb[ii].a = tb[ii].b ; tb[ii].b = tt ; } return ; } /*---------------------------------------------------------------------------*/ void sound_write_au_header( FILE *fp , int nn , int srate , int code ) { uint32_t ival ; set_little_endian() ; /* do we need to swap bytes in header output? */ fwrite( ".snd" , 1 , 4, fp ) ; /*----- magic 'number' -----*/ ival = 32 ; if( little_endian ) ival = swap_fourby(ival) ; fwrite( &ival , 1 , 4 , fp ) ; /*----- offset to data -----*/ ival = nn ; if( little_endian ) ival = swap_fourby(ival) ; fwrite( &ival , 1 , 4 , fp ) ; /*----- number of data bytes -----*/ ival = code ; if( little_endian ) ival = swap_fourby(ival) ; fwrite( &ival , 1 , 4 , fp ) ; /*----- encoding -----*/ ival = srate ; if( little_endian ) ival = swap_fourby(ival) ; fwrite( &ival , 1 , 4 , fp ) ; /*----- samples per second -----*/ ival = 1 ; if( little_endian ) ival = swap_fourby(ival) ; fwrite( &ival , 1 , 4 , fp ) ; /*----- number of channels -----*/ fwrite( "AFNI.au" , 1 , 8 , fp ) ; /*----- 8 bytes of annotation -----*/ return ; } /*---------------------------------------------------------------------------*/ /* samples in aa[] are between -1 and 1. sample rate (per second) is in srate. scl is a scale down factor (0 < scl <= 1). *//*-------------------------------------------------------------------------*/ void sound_write_au_ulaw( char *fname, int nn, float *aa, int srate, float scl ) { FILE *fp ; uint32_t ival , jval ; byte *bb ; int ii ; /* check inputs */ if( fname == NULL || nn < 2 || aa == NULL ){ ERROR_message("Illegal inputs to sound_write_au :(") ; return ; } if( srate < 8000 ) srate = DEFAULT_SRATE ; if( scl < 0.0f || scl > 1.0f ) scl = 1.0f ; /* open output file */ fp = fopen( fname , "w" ) ; if( fp == NULL ){ ERROR_message("Can't open audio output file '%s'",fname) ; return ; } /* write .au file header */ sound_write_au_header( fp , nn , srate , 1 ) ; /* convert and write data */ bb = (byte *)malloc(sizeof(byte)*nn) ; for( ii=0 ; ii < nn ; ii++ ) bb[ii] = mulaw(scl*aa[ii]) ; fwrite( bb , 1 , nn , fp ) ; /* done done done */ fclose(fp) ; free(bb) ; return ; } /*---------------------------------------------------------------------------*/ /* samples in aa[] are between -1 and 1. sample rate (per second) is in srate. scl is a scale down factor (0 < scl <= 1). *//*-------------------------------------------------------------------------*/ void sound_write_au_8PCM( char *fname, int nn, float *aa, int srate, float scl ) { FILE *fp ; uint32_t ival , jval ; int8_t *bb ; int ii ; float fac , val ; /* check inputs */ if( fname == NULL || nn < 2 || aa == NULL ){ ERROR_message("Illegal inputs to sound_write_au :(") ; return ; } if( srate < 8000 ) srate = DEFAULT_SRATE ; if( scl < 0.0f || scl > 1.0f ) scl = 1.0f ; /* open output file */ fp = fopen( fname , "w" ) ; if( fp == NULL ){ ERROR_message("Can't open audio output file '%s'",fname) ; return ; } /* write .au file header */ sound_write_au_header( fp , nn , srate , 2 ) ; /* convert and write data */ bb = (int8_t *)malloc(sizeof(int8_t)*nn) ; fac = 127.444f * scl ; for( ii=0 ; ii < nn ; ii++ ){ val = fac*aa[ii] ; bb[ii] = ( val < -127.0f ) ? -127 : ( val > 127.0f ) ? 127 : (int8_t)rintf(val) ; } fwrite( bb , 1 , nn , fp ) ; /* done done done */ fclose(fp) ; free(bb) ; return ; } /*---------------------------------------------------------------------------*/ /* samples in aa[] are between -1 and 1. sample rate (per second) is in srate. scl is a scale down factor (0 < scl <= 1). *//*-------------------------------------------------------------------------*/ void sound_write_au_16PCM( char *fname, int nn, float *aa, int srate, float scl ) { FILE *fp ; uint32_t ival , jval ; int16_t *bb ; int ii ; float fac , val ; /* check inputs */ if( fname == NULL || nn < 2 || aa == NULL ){ ERROR_message("Illegal inputs to sound_write_au :(") ; return ; } if( srate < 8000 ) srate = DEFAULT_SRATE ; if( scl < 0.0f || scl > 1.0f ) scl = 1.0f ; /* open output file */ fp = fopen( fname , "w" ) ; if( fp == NULL ){ ERROR_message("Can't open audio output file '%s'",fname) ; return ; } /* write .au file header */ sound_write_au_header( fp , 2*nn , srate , 3 ) ; /* convert and write data */ bb = (int16_t *)malloc(sizeof(int16_t)*nn) ; fac = 32767.444f * scl ; for( ii=0 ; ii < nn ; ii++ ){ val = fac*aa[ii] ; bb[ii] = ( val < -32767.0f ) ? -32767 : ( val > 32767.0f ) ? 32767 : (int16_t)rintf(val) ; } if( little_endian ) swap_twobyar( nn , bb ) ; fwrite( bb , 2 , nn , fp ) ; /* done done done */ fclose(fp) ; free(bb) ; return ; } /*---------------------------------------------------------------------------*/ #undef A4 #define A4 440.0 /* Hz */ MRI_IMAGE * mri_sound_1D_to_FM( MRI_IMAGE *imin , float fbot , float ftop , int srate , int nsper ) { float *aa,*bb ; float abot,atop , lfbot,lftop,dlf,dfac,ai,ap,di,dp ; float afac,dph,ph , val ; int nout , ii,jj,kk,ip , nn ; MRI_IMAGE *imout , *qim ; if( imin == NULL ) return NULL ; nn = imin->nx ; if( nn < 2 ){ ERROR_message("mri_sound_1D_to_FM: nn = %d",nn) ; return NULL ; } if( imin->kind != MRI_float ){ qim = mri_to_float(imin) ; } else { qim = imin ; } aa = MRI_FLOAT_PTR(qim) ; if( fbot <= 0.0f || fbot >= ftop ){ fbot = 0.25f * A4 ; ftop = 4.0f * A4 ; } if( ftop > 10000.0f ) ftop = 10000.0f ; if( fbot < 30.0f ) fbot = 30.0f ; if( srate < 8000 ) srate = DEFAULT_SRATE ; if( nsper <= 0 ) nsper = 1 ; /* will scale so abot..atop = log(fbot)..log(ftop) */ abot = atop = aa[0] ; for( ii=1 ; ii < nn ; ii++ ){ if( aa[ii] < abot ) abot = aa[ii] ; else if( aa[ii] > atop ) atop = aa[ii] ; } nout = nsper * nn ; imout = mri_new( nout , 1 , MRI_float ) ; bb = MRI_FLOAT_PTR(imout) ; if( abot >= atop ){ float fmid = sqrtf(fbot*ftop) ; INFO_message("mri_sound_1D_to_FM: only 1 value == sine wave output f=%.1g",fmid) ; ph = 0.0f ; dph = 2.0f * PI * fmid / srate ; for( ii=0 ; ii < nout ; ii++ ){ bb[ii] = sin(ph) ; ph += dph ; } if( qim != imin ) mri_free(qim) ; return imout ; } lfbot = log2(fbot) ; lftop = log2(ftop) ; dlf = lftop-lfbot ; dfac = 1.0f / nsper ; afac = dlf / (atop-abot) ; #undef FRMOD #define FRMOD(x) exp2f( lfbot + afac*((x)-abot) ) ph = 0.0f ; dph = 2.0f * PI / srate ; for( jj=ii=0 ; ii < nn ; ii++ ){ ph += FRMOD(aa[ii]) * dph ; bb[jj++] = sin(ph) ; #if 0 val = FRMOD(aa[ii]) ; ININFO_message(" %g %g",val,bb[jj-1]) ; #endif if( nsper > 1 ){ ip = ii+1 ; if( ip >= nn ) ip = nn-1 ; ai = aa[ii] ; ap = aa[ip] ; di = 1.0f ; dp = 0.0f ; for( kk=1 ; kk < nsper ; kk++ ){ di -= dfac ; dp += dfac ; ph += FRMOD( di*ai+dp*ap ) * dph ; bb[jj++] = sin(ph) ; #if 0 val = FRMOD( di*ai+dp*ap ) ; ININFO_message(" %g %g",val,bb[jj-1]) ; #endif } } } #undef FRMOD if( qim != imin ) mri_free(qim) ; return imout ; } #undef A4 /*---------------------------------------------------------------------------*/ #define PA 54.0f /* 5 base notes (Hz) */ #define PC 64.0f #define PD 72.0f #define PE 81.0f #define PG 96.0f #define POCTA 4 /* number of octaves */ static int npenta = 0 ; /* will be 5*POCTA+1 */ static float *penta = NULL ; /* will hold all the notes */ void sound_setup_penta( int octstart ) { int ii , jj ; float fac ; if( npenta > 0 ) return ; if( octstart < 0 || octstart >= POCTA ) octstart = 0 ; npenta = 5*POCTA+1 ; penta = (float *)malloc(sizeof(float)*npenta) ; fac = 1.0f ; for( jj=ii=0 ; ii < POCTA ; ii++ ){ if( ii >= octstart ){ penta[jj++] = fac * PA ; penta[jj++] = fac * PC ; penta[jj++] = fac * PD ; penta[jj++] = fac * PE ; penta[jj++] = fac * PG ; } fac *= 2.0f ; } penta[jj++] = fac * PA ; /* complete last octave */ npenta = jj ; return ; } /*---------------------------------------------------------------------------*/ static int note_waveform = SOUND_WAVEFORM_TRIANGLE ; void sound_set_note_waveform( int nn ){ note_waveform = nn; } #undef twoPI #define twoPI 6.283185f #undef fourPI #define fourPI 12.566371f #undef sixPI #define sixPI 18.849556f /* wav_xxx functions have period 1, amplitude 1, and assume t > 0 */ static INLINE float wav_sine(float t){ return ( 0.999f*sinf(twoPI*t) ) ; } static INLINE float wav_h2sine(float t){ return ( 0.953f*sinf(twoPI*t)+0.3f*cosf(fourPI*t) ) ; } static INLINE float wav_sqsine(float t){ float val = sinf(twoPI*t) , aval ; #if 1 aval = sqrtf(fabsf(val)) ; #else aval = powf( fabsf(val) , 0.6f ) ; #endif return ( (val >= 0.0f ) ? aval : -aval ) ; } static INLINE float wav_square(float t){ float dt = fmodf(t,1.0f) ; return ( (dt < 0.45f) ? 0.999f :(dt < 0.55f) ? 0.999f-19.98f*(dt-0.45f) : -0.999f ) ; } #undef TW #define TW(x) (0.999f-3.996f*fabsf(x)) static INLINE float wav_triangle(float t){ float dt = fmodf(t,1.0f) ; return ( (dt < 0.5f) ? TW(dt-0.25f) : -TW(dt-0.75f) ) ; } #undef envA #undef envD #undef envS #define envA 0.0666f #define envD 0.1666f #define envS 0.9333f /* Envelope for note, with 0 <= t <= 1 */ static INLINE float ADSR_env(float t){ if( t <= envA ) return (0.07f+0.93f*t/envA) ; else if( t <= envD ) return (1.0f-0.1f*(t-envD)/(envD-envA)) ; else if( t <= envS ) return (0.9f) ; return (0.9f-0.83f*(t-envS)/(1.0f-envS)) ; } static int use_ADSR = 1 ; void sound_set_note_ADSR(int qq){ use_ADSR = qq ; } /*---------------------------------------------------------------------------*/ /* Note that if frq >= 0.5*srate, bad things will happen due to Mr Nyquist!! */ /*---------------------------------------------------------------------------*/ static float ttn = 0.0f ; /* continuous time between notes */ static void reset_note_ttn(void){ ttn = 0.0f ; } void sound_make_note( float frq, int waveform, int srate, int nsam, float *sam ) { int ii ; float dt,tt ; if( frq <= 0.0f || nsam < 9 || sam == NULL ) return ; if( srate < 8000 ) srate = DEFAULT_SRATE ; dt = frq / srate ; tt = ttn ; /* form periodic waveform */ switch( waveform ){ default: case SOUND_WAVEFORM_SINE: for( ii=0 ; ii < nsam ; ii++ ){ sam[ii] = wav_sine(tt); tt += dt; } break ; case SOUND_WAVEFORM_SQUARE: for( ii=0 ; ii < nsam ; ii++ ){ sam[ii] = wav_square(tt); tt += dt; } break ; case SOUND_WAVEFORM_TRIANGLE: for( ii=0 ; ii < nsam ; ii++ ){ sam[ii] = wav_triangle(tt); tt += dt; } break ; case SOUND_WAVEFORM_H2SINE: for( ii=0 ; ii < nsam ; ii++ ){ sam[ii] = wav_h2sine(tt); tt += dt; } break ; case SOUND_WAVEFORM_SQSINE: for( ii=0 ; ii < nsam ; ii++ ){ sam[ii] = wav_sqsine(tt); tt += dt; } break ; } ttn = tt ; /* continuous time between notes */ /* apply envelope (if note has enough samples) */ if( nsam > 127 && use_ADSR ){ dt = 1.0f/nsam ; tt= 0.0f ; for( ii=0 ; ii < nsam ; ii++ ){ sam[ii] *= ADSR_env(tt); tt += dt; } } else if( nsam > 24 ){ #if 0 float fac ; for( ii=0 ; ii < 9 ; ii++ ){ fac = (ii+1)*0.1f ; sam[ii] *= fac ; sam[nsam-1-ii] *= fac ; } #endif } return ; } /*---------------------------------------------------------------------------*/ #define VAL_TO_CODE(v) ((int)rintf((v)-SOUND_WAVECODE_BASE)) MRI_IMAGE * mri_sound_1D_to_notes( MRI_IMAGE *imin, int srate, int nsper, int ny, int ignore, int use_wavecodes ) { float *aa,*bb,*qa ; float abot,atop , fac , *valn ; int nout , ii,jj , nn , qq ; MRI_IMAGE *imout , *qim ; int max_wavecode=0 ; int ncode=0 , nsamcode=0 ; /*--- deal with inputs ---*/ if( imin == NULL ) return NULL ; nn = imin->nx ; if( nn < 2 ){ ERROR_message("mri_sound_1D_to_notes: nn = %d",nn) ; return NULL ; } if( ignore < 0 || nn-ignore < 2 ) ignore = 0 ; if( imin->kind != MRI_float ){ qim = mri_to_float(imin) ; } else { qim = imin ; } aa = MRI_FLOAT_PTR(qim) ; if( ny <= 0 ) ny = 1 ; else if( ny > qim->ny ) ny = qim->ny ; if( srate < 8000 ) srate = DEFAULT_SRATE ; if( nsper <= 9 ) nsper = srate/2 ; /*-- check for wavecodes embedded in the data --*/ if( use_wavecodes ){ max_wavecode = get_num_sound_waveforms() ; use_wavecodes = max_wavecode > 0 ; } if( use_wavecodes ){ } /*-- create output ---*/ nout = nsper * (nn-ignore) ; imout = mri_new( nout , 1 , MRI_float ) ; bb = MRI_FLOAT_PTR(imout) ; /* is full of zeros */ sound_setup_penta(1) ; /* skip octave 0 -- it's too low */ valn = (float *)malloc(sizeof(float)*nsper) ; /* notes */ for( qq=0 ; qq < ny ; qq++ ){ /* process first ny columns */ qa = aa + (nn*qq) ; /* add their sounds all up */ abot = atop = qa[ignore] ; reset_note_ttn() ; /* re-start time at 0 */ for( ii=ignore+1 ; ii < nn ; ii++ ){ if( qa[ii] < abot ) abot = qa[ii] ; else if( qa[ii] > atop ) atop = qa[ii] ; } if( abot < atop ){ /* some range to the numbers */ fac = (npenta-1.0f) / (atop-abot) ; for( ii=ignore ; ii < nn ; ii++ ){ jj = rintf( fac*(qa[ii]-abot) ) ; sound_make_note( penta[jj], note_waveform, srate, nsper, valn ) ; for( jj=0 ; jj < nsper ; jj++ ) bb[((ii-ignore)*nsper)+jj] += valn[jj]; } } } abot = mri_maxabs(imout) ; if( abot == 0.0f ){ /* nothing computed? do something random! */ for( ii=ignore ; ii < nn ; ii++ ){ jj = lrand48() % npenta ; sound_make_note( penta[jj], note_waveform, srate, nsper, bb+((ii-ignore)*nsper) ) ; } } THD_normmax( nout , bb ) ; /* max abs value = 1 */ free(valn) ; if( qim != imin ) mri_free(qim) ; return imout ; }