autoCochleagram Sound_to_Cochleagram (Sound me, double dt, double df, double dt_window, double forwardMaskingTime) { try { double duration = my nx * my dx; long nFrames = 1 + (long) floor ((duration - dt_window) / dt); long nsamp_window = (long) floor (dt_window / my dx), halfnsamp_window = nsamp_window / 2 - 1; long nf = lround (25.6 / df); double dampingFactor = forwardMaskingTime > 0.0 ? exp (- dt / forwardMaskingTime) : 0.0; // default 30 ms double integrationCorrection = 1.0 - dampingFactor; nsamp_window = halfnsamp_window * 2; if (nFrames < 2) return autoCochleagram (); double t1 = my x1 + 0.5 * (duration - my dx - (nFrames - 1) * dt); // centre of first frame autoCochleagram thee = Cochleagram_create (my xmin, my xmax, nFrames, dt, t1, df, nf); autoSound window = Sound_createSimple (1, nsamp_window * my dx, 1.0 / my dx); for (long iframe = 1; iframe <= nFrames; iframe ++) { double t = Sampled_indexToX (thee.get(), iframe); long leftSample = Sampled_xToLowIndex (me, t); long rightSample = leftSample + 1; long startSample = rightSample - halfnsamp_window; long endSample = rightSample + halfnsamp_window; if (startSample < 1) { Melder_casual (U"Start sample too small: ", startSample, U" instead of 1."); startSample = 1; } if (endSample > my nx) { Melder_casual (U"End sample too small: ", endSample, U" instead of ", my nx, U"."); endSample = my nx; } /* Copy a window to a frame. */ for (long i = 1; i <= nsamp_window; i ++) window -> z [1] [i] = ( my ny == 1 ? my z[1][i+startSample-1] : 0.5 * (my z[1][i+startSample-1] + my z[2][i+startSample-1]) ) * (0.5 - 0.5 * cos (2.0 * NUMpi * i / (nsamp_window + 1))); autoSpectrum spec = Sound_to_Spectrum (window.get(), true); autoExcitation excitation = Spectrum_to_Excitation (spec.get(), df); for (long ifreq = 1; ifreq <= nf; ifreq ++) thy z [ifreq] [iframe] = excitation -> z [1] [ifreq] + ( iframe > 1 ? dampingFactor * thy z [ifreq] [iframe - 1] : 0 ); } for (long iframe = 1; iframe <= nFrames; iframe ++) for (long ifreq = 1; ifreq <= nf; ifreq ++) thy z [ifreq] [iframe] *= integrationCorrection; return thee; } catch (MelderError) { Melder_throw (me, U": not converted to Cochleagram."); } }
Cochleagram Sound_to_Cochleagram_edb (Sound me, double dtime, double dfreq, int hasSynapse, double replenishmentRate, double lossRate, double returnRate, double reprocessingRate) { try { double duration_seconds = my xmax; if (dtime < my dx) dtime = my dx; long ntime = floor (duration_seconds / dtime + 0.5); if (ntime < 2) return NULL; long nfreq = floor (25.6 / dfreq + 0.5); // 25.6 Bark = highest frequency autoCochleagram thee = Cochleagram_create (my xmin, my xmax, ntime, dtime, 0.5 * dtime, dfreq, nfreq); /* Stages 1 and 2: outer- and middle-ear filtering. */ /* From acoustic sound to oval window. */ for (long ifreq = 1; ifreq <= nfreq; ifreq ++) { double *response = thy z [ifreq]; /* Stage 3: basilar membrane filtering by gammatones. */ /* From oval window to basilar membrane response. */ double midFrequency_Bark = (ifreq - 0.5) * dfreq; double midFrequency_Hertz = Excitation_barkToHertz (midFrequency_Bark); autoSound gammatone = createGammatone (midFrequency_Hertz, 1 / my dx); autoSound basil = Sounds_convolve (me, gammatone.peek(), kSounds_convolve_scaling_SUM, kSounds_convolve_signalOutsideTimeDomain_ZERO); /* Stage 4: detection = rectify + integrate + low-pass 500 Hz. */ /* From basilar membrane response to firing rate. */ if (hasSynapse) { double dt = my dx; double M = 1; /* Maximum free transmitter. */ double A = 5, B = 300, g = 2000; /* Determine permeability. */ double y = replenishmentRate; /* Meddis: 5.05 */ double l = lossRate, r = returnRate; /* Meddis: 2500, 6580 */ double x = reprocessingRate; /* Meddis: 66.31 */ double h = 50000; /* Convert cleft contents to firing rate. */ double gdt = 1 - exp (- g * dt), ydt = 1 - exp (- y * dt), ldt = (1 - exp (- (l + r) * dt)) * l / (l + r), rdt = (1 - exp (- (l + r) * dt)) * r / (l + r), xdt = 1 - exp (- x * dt); double kt = g * A / (A + B); /* Membrane permeability. */ double c = M * y * kt / (l * kt + y * (l + r)); /* Cleft contents. */ double q = c * (l + r) / kt; /* Free transmitter. */ double w = c * r / x; /* Reprocessing store. */ for (long itime = 1; itime <= basil -> nx; itime ++) { double splusA = basil -> z [1] [itime] * 10 + A; double replenish = M > q ? ydt * (M - q) : 0; double eject, loss, reuptake, reprocess; kt = splusA > 0 ? gdt * splusA / (splusA + B) : 0; eject = kt * q; loss = ldt * c; reuptake = rdt * c; reprocess = xdt * w; q = q + replenish - eject + reprocess; c = c + eject - loss - reuptake; w = w + reuptake - reprocess; basil -> z [1] [itime] = h * c; } } if (dtime == my dx) { for (long itime = 1; itime <= ntime; itime ++) response [itime] = basil -> z [1] [itime]; } else { double d = dtime / basil -> dx / 2; double factor = -6 / d / d; double area = d * sqrt (NUMpi / 6); double expmin6 = exp (-6), onebyoneminexpmin6 = 1 / (1 - expmin6); for (long itime = 1; itime <= ntime; itime ++) { double t1 = (itime - 1) * dtime, t2 = t1 + dtime, mean = 0; long i1, i2; long n = Matrix_getWindowSamplesX (basil.peek(), t1, t2, & i1, & i2); Melder_assert (n >= 1); if (n <= 2) { for (long isamp = i1; isamp <= i2; isamp ++) mean += basil -> z [1] [isamp]; mean /= n; } else { double mu = floor ((i1 + i2) / 2.0); long muint = mu, dint = d; for (long isamp = muint - dint; isamp <= muint + dint; isamp ++) { double y = 0; if (isamp < 1 || isamp > basil -> nx) Melder_casual ("isamp %ld", isamp); else y = basil -> z [1] [isamp]; mean += y * onebyoneminexpmin6 * (exp (factor * (isamp - muint) * (isamp - muint)) - expmin6); } mean /= area; } response [itime] = mean; } } } return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": not converted to Cochleagram (edb)."); } }