public FingerprintSignature CreateAudioFingerprint(string key, string filename, int startPositionInMS, int toReadInMS) { SpectrogramConfig spectrogramConfig = new DefaultSpectrogramConfig(); AudioSamples samples = null; try { // First read audio file and downsample it to mono 5512hz samples = audioEngine.ReadMonoFromFile(filename, spectrogramConfig.SampleRate, startPositionInMS, toReadInMS); } catch { return(null); } // No slice the audio is chunks seperated by 11,6 ms (5512hz 11,6ms = 64 samples!) // An with length of 371ms (5512kHz 371ms = 2048 samples [rounded]) FingerprintSignature fingerprint = audioEngine.CreateFingerprint(samples, spectrogramConfig); if (fingerprint != null) { fingerprint.Reference = key; } return(fingerprint); }
/// <summary> /// Read audio file and mix it to mono, then resample it to "samplerate" /// </summary> /// <param name="filename">Name of the file</param> /// <param name="samplerate">Output sample rate, default 5512hz</param> /// <param name="toReadInMS">Milliseconds to read of or <= 0 to read everything</param> /// <param name="startmillisecond">Start position in millisecond, default 0 for begin</param> /// <returns>Array of samples</returns> /// <remarks> /// Seeking capabilities of Bass where not used because of the possible /// timing errors on different formats. /// </remarks> public AudioSamples ReadMonoFromFile(string filename, int outputSamplerate = 5512, int startPositionInMS = 0, int toReadInMS = -1) { if (!File.Exists(filename)) { throw new Exception("File '" + filename + "' doesn't exists."); } // calculate total milliseconds to read int totalmilliseconds = toReadInMS <= 0 ? Int32.MaxValue : (toReadInMS + startPositionInMS); float[] data = null; //create streams for re-sampling int stream = CreateStream(filename); int mixerStream = CreateMixerStream(outputSamplerate); if (!bassService.CombineMixerStreams(mixerStream, stream, BASSFlag.BASS_MIXER_FILTER | BASSFlag.BASS_MIXER_DOWNMIX)) { throw new BassException(bassService.GetLastError()); } int bufferSize = outputSamplerate * 10 * 4; /*read 10 seconds at each iteration*/ float[] buffer = new float[bufferSize]; List <float[]> chunks = new List <float[]>(); int size = 0; while ((float)(size) / outputSamplerate * 1000 < totalmilliseconds) { // get re-sampled/mono data int bytesRead = Bass.BASS_ChannelGetData(mixerStream, buffer, bufferSize); if (bytesRead == 0) { break; } float[] chunk = new float[bytesRead / 4]; //each float contains 4 bytes Array.Copy(buffer, chunk, bytesRead / 4); chunks.Add(chunk); size += bytesRead / 4; //size of the data } //while if ((float)(size) / outputSamplerate * 1000 < (toReadInMS + startPositionInMS)) { // not enough samples to return the requested data return(null); } // Do bass cleanup Bass.BASS_ChannelStop(mixerStream); Bass.BASS_ChannelStop(stream); Bass.BASS_StreamFree(mixerStream); Bass.BASS_StreamFree(stream); int start = (int)((float)startPositionInMS * outputSamplerate / 1000); int end = (toReadInMS <= 0) ? size : (int)((float)(startPositionInMS + toReadInMS) * outputSamplerate / 1000); data = new float[size]; int index = 0; // Concatenate foreach (float[] chunk in chunks) { Array.Copy(chunk, 0, data, index, chunk.Length); index += chunk.Length; } // Select specific part of the song if (start != 0 || end != size) { float[] temp = new float[end - start]; Array.Copy(data, start, temp, 0, end - start); data = temp; } // Create audiosamples object AudioSamples audioSamples = new AudioSamples(); audioSamples.Origin = filename; audioSamples.Channels = 1; audioSamples.SampleRate = outputSamplerate; audioSamples.StartInMS = start; audioSamples.DurationInMS = end; audioSamples.Samples = data; return(audioSamples); }
/// <summary> /// Stretches audio, keeps channels and samplerate. /// negatief value slowsdown (makes longer) the audio /// Positief value speedsup (make shorter) the audio /// </summary> public AudioSamples TimeStretch(float[] inputAudioSamples, int inputSampleRate = 44100, int inputChannels = 2, float rateFactor = 0.0f) { // calculate total milliseconds to read int totalmilliseconds = Int32.MaxValue; float[] data = null; int stream = Bass.BASS_StreamCreatePush(inputSampleRate, inputChannels, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, IntPtr.Zero); ThrowIfStreamIsInvalid(stream); BASS_CHANNELINFO channelInfo = Bass.BASS_ChannelGetInfo(stream); Bass.BASS_StreamPutData(stream, inputAudioSamples, inputAudioSamples.Length * 4); SoundTouch <Single, Double> soundTouch = new SoundTouch <Single, Double>(); soundTouch.SetSampleRate(channelInfo.freq); soundTouch.SetChannels(channelInfo.chans); soundTouch.SetTempoChange(0.0f); soundTouch.SetPitchSemiTones(0.0f); soundTouch.SetRateChange(rateFactor); // -1.4f = Radio 538 setting soundTouch.SetSetting(SettingId.UseQuickseek, 0); soundTouch.SetSetting(SettingId.UseAntiAliasFilter, 0); int bufferSize = 2048; float[] buffer = new float[bufferSize]; List <float[]> chunks = new List <float[]>(); int size = 0; int nSamples = 0; while ((float)(size) / channelInfo.freq * 1000 < totalmilliseconds) { // get re-sampled data int bytesRead = Bass.BASS_ChannelGetData(stream, buffer, bufferSize); if (bytesRead <= 0) { break; } nSamples = (bytesRead / 4) / channelInfo.chans; // Feed the samples into SoundTouch processor soundTouch.PutSamples(buffer, nSamples); // Read ready samples from SoundTouch processor & write them output file. // NOTES: // - 'receiveSamples' doesn't necessarily return any samples at all // during some rounds! // - On the other hand, during some round 'receiveSamples' may have more // ready samples than would fit into 'sampleBuffer', and for this reason // the 'receiveSamples' call is iterated for as many times as it // outputs samples. do { nSamples = soundTouch.ReceiveSamples(buffer, (bufferSize / channelInfo.chans)); if (nSamples > 0) { float[] chunk = new float[nSamples * channelInfo.chans]; Array.Copy(buffer, chunk, nSamples * channelInfo.chans); chunks.Add(chunk); size += nSamples * channelInfo.chans; //size of the data } } while (nSamples != 0); } //while // Now the input file is processed, yet 'flush' few last samples that are // hiding in the SoundTouch's internal processing pipeline. soundTouch.Flush(); do { nSamples = soundTouch.ReceiveSamples(buffer, (bufferSize / channelInfo.chans)); if (nSamples > 0) { float[] chunk = new float[nSamples * channelInfo.chans]; Array.Copy(buffer, chunk, nSamples * channelInfo.chans); chunks.Add(chunk); size += nSamples * channelInfo.chans; //size of the data } } while (nSamples != 0); if (size <= 0) { // not enough samples to return the requested data return(null); } // Do bass cleanup Bass.BASS_ChannelStop(stream); Bass.BASS_StreamFree(stream); int start = 0; int end = size; data = new float[size]; int index = 0; // Concatenate foreach (float[] chunk in chunks) { Array.Copy(chunk, 0, data, index, chunk.Length); index += chunk.Length; } // Select specific part of the song if (start != 0 || end != size) { float[] temp = new float[end - start]; Array.Copy(data, start, temp, 0, end - start); data = temp; } // Create audiosamples object AudioSamples audioSamples = new AudioSamples(); audioSamples.Origin = "MEMORY"; audioSamples.Channels = channelInfo.chans; audioSamples.SampleRate = channelInfo.freq; audioSamples.StartInMS = start; audioSamples.DurationInMS = end; audioSamples.Samples = data; return(audioSamples); }
public FingerprintSignature CreateFingerprint(AudioSamples audioSamples, SpectrogramConfig configuration) { absEnergy = new double[(Frequencies.Length - 1) * 4]; // number of frequencies bands (33) lowpassFilters = new LowpassFilters(Frequencies.Length - 1); // 33 bands try { int width = (audioSamples.Samples.Length - configuration.WdftSize) / configuration.Overlap; // WdftSize=2048 / Overlap=64 if (width < 1) { return(null); } // reserve memory for 32 bit fingerprint hashes byte[] hashes = new byte[width * sizeof(uint)]; byte[] reliabilities = new byte[width * 32]; // elke subfinger (32 bits) heeft voor elke bit een waarde tussen 0 en 31 voor relibility (dus 5 bits), we ronden dit af naar 1 byte FingerprintSignature fingerprintSignature = new FingerprintSignature(null, 0, hashes, (long)(width * 11.6)); fingerprintSignature.Reliabilities = reliabilities; // Calculate a hamming windows float[] hammingWindow = new float[configuration.WdftSize]; for (int i = 0; i < hammingWindow.Length; i++) { // Hamming (watch it peak is at beginning not as in real hamming in the middle) hammingWindow[i] = 0.54f + 0.46f * (float)System.Math.Cos((6.283f * (float)i / hammingWindow.Length)); //hammingWindow[i] = 0.54f - (0.46f * (float)System.Math.Cos(((6.283f * (float)i) / (hammingWindow.Length - 1)))); // real hamming window } //for int[] frequenciesRange = Frequencies; // 34 freqencies float[] samples = new float[configuration.WdftSize]; for (int i = 0; i < width; i++) { if (((samples.Length - 1) + (i * configuration.Overlap)) >= audioSamples.Samples.Length) { // we hebben niet voldoende data meer! // dus we stoppen nu en nemen het "laaste" stukje niet mee! break; } for (int j = 0; j < samples.Length; j++) { samples[j] = audioSamples.Samples[j + (i * configuration.Overlap)] * hammingWindow[j]; } // for j float[] complexSignal = fftService.FFTForward(samples, 0, configuration.WdftSize); byte[] reliability; uint subFingerprint = CalculateSubFingerprint(complexSignal, frequenciesRange, 5512, out reliability); Buffer.BlockCopy(BitConverter.GetBytes(subFingerprint), 0, hashes, i * sizeof(uint), sizeof(uint)); Buffer.BlockCopy(reliability, 0, reliabilities, i * reliability.Length, reliability.Length); // sequencenumber = i; // timestamp = (i / audioSamples.Samples.Length) * configuration.SampleRate } //for return(fingerprintSignature); } finally { absEnergy = null; lowpassFilters = null; } }
/// <summary> /// Resample to new samplerate and in mono /// </summary> public AudioSamples Resample(float[] inputAudioSamples, int inputSampleRate = 44100, int inputChannels = 2, int outputSamplerate = 5512) { //create streams for re-sampling int stream = Bass.BASS_StreamCreatePush(inputSampleRate, inputChannels, GetDefaultFlags(), IntPtr.Zero); ThrowIfStreamIsInvalid(stream); int mixerStream = BassMix.BASS_Mixer_StreamCreate(outputSamplerate, 1, GetDefaultFlags()); ThrowIfStreamIsInvalid(mixerStream); if (!bassService.CombineMixerStreams(mixerStream, stream, BASSFlag.BASS_MIXER_FILTER | BASSFlag.BASS_MIXER_DOWNMIX)) { throw new BassException(bassService.GetLastError()); } Bass.BASS_StreamPutData(stream, inputAudioSamples, inputAudioSamples.Length * 4); int bufferSize = outputSamplerate * 10 * 4; /*read 10 seconds at each iteration*/ float[] buffer = new float[bufferSize]; List <float[]> chunks = new List <float[]>(); int size = 0; int bytesRead; do { // get re-sampled/mono data bytesRead = Bass.BASS_ChannelGetData(mixerStream, buffer, bufferSize); if (bytesRead == 0) { break; } float[] chunk = new float[bytesRead / 4]; //each float contains 4 bytes Array.Copy(buffer, chunk, bytesRead / 4); chunks.Add(chunk); size += bytesRead / 4; //size of the data } while (bytesRead > 0); // Do bass cleanup Bass.BASS_ChannelStop(mixerStream); Bass.BASS_ChannelStop(stream); Bass.BASS_StreamFree(mixerStream); Bass.BASS_StreamFree(stream); float[] data = new float[size]; int index = 0; // Concatenate foreach (float[] chunk in chunks) { Array.Copy(chunk, 0, data, index, chunk.Length); index += chunk.Length; } // Create audiosamples object AudioSamples audioSamples = new AudioSamples(); audioSamples.Origin = "MEMORY"; audioSamples.Channels = 1; audioSamples.SampleRate = outputSamplerate; audioSamples.StartInMS = 0; audioSamples.DurationInMS = (int)(((float)size / (float)outputSamplerate) * 1000.0f); audioSamples.Samples = data; return(audioSamples); }