/// <summary> /// Construct with configuration and output stream writer /// </summary> /// <param name="config"></param> public Callback(AnalyserConfig config) { this.config = config; // sps is assumed to be even var sps = config.SamplesPerSlot; channelData = new Channel[Analyser.CHANNELS]; for (var c = 0; c < Analyser.CHANNELS; c++) { channelData[c] = new Channel(config); } window0 = new double[sps]; window1 = new double[sps]; window2 = new double[sps]; // Build a triangular window across 3 slots var half = sps * 3 >> 1; var end = (sps << 1) - 1; var step = 1.0 / (half - 1); for (var i = 0; i < sps; i++) { window0[i] = window2[sps - 1 - i] = i * step; } for (var i = sps; i < half; i++) { window1[i - sps] = window1[end - i] = i * step; } }
/// <summary> /// Generate an audio fingerprint for each selected input audio channel in the buffer /// </summary> /// <param name="buffer"></param> /// <returns></returns> internal unsafe ulong[] GetAudioFingerprints(IntPtr buffer, AnalyserConfig config) { var signatures = new ulong[Analyser.CHANNELS]; // window support var support = config.SamplesPerSlot * 3; for (var channel = 0; channel < Analyser.CHANNELS; channel++) { var signature = 0UL; var signalPresent = false; var cd = channelData[channel]; // Initialise sptr to the first sample for the channel var sptr = (short *)(buffer) + channel; var bit = 1UL << config.SlotsPerFrame; for (var slot = 0; slot < config.SlotsPerFrame; slot++) { // Sum of squares of windowed samples var ssqr = 0.0; for (var i = 0; i < config.SamplesPerSlot; i++) { var sample = *sptr; // Step to the same channel in the next sample sptr += Analyser.CHANNELS; var fsample = cd.Filter.Filter(sample); cd.Slot2[i] = fsample * fsample; ssqr += window0[i] * cd.Slot0[i] + window1[i] * cd.Slot1[i] + window2[i] * cd.Slot2[i]; } signalPresent = signalPresent || (ssqr > 0); // Convert to logarithmic RMS // The log scaling ensures that we are working with perceived loudness var rms = signalPresent ? Math.Log10(ssqr / support) : 0.0; // Rotate the slot buffers var tmp = cd.Slot0; cd.Slot0 = cd.Slot1; cd.Slot1 = cd.Slot2; cd.Slot2 = tmp; bit >>= 1; var rmsa = cd.RmsBuffer.Average(); if (rms > rmsa) { signature |= bit; } Buffer.BlockCopy(cd.RmsBuffer, sizeof(double), cd.RmsBuffer, 0, (config.FirLength - 1) * sizeof(double)); cd.RmsBuffer[config.FirLength - 1] = rms; } // MSB is a signal present flag if (signalPresent) { signature |= 0x8000000000000000UL; } signatures[channel] = signature; } return(signatures); }
public Analyser(AnalyserConfig config) { this.config = config; }