private bool HasPotentialHumanSpeech(int visualChannel, long pos) { Bass.ChannelSetPosition(visualChannel, pos); const int scaleFactor = _scaleFactorSqr * ushort.MaxValue; var buffer = new float[2048]; var bufferResult = _isMixerUsed ? BassMix.ChannelGetData(visualChannel, buffer, (int)_maxFft) : Bass.ChannelGetData(visualChannel, buffer, (int)_maxFft); var verticalSamplesWithSound = 0; if (bufferResult > 0) { for (var n = 0; n < buffer.Length; n++) { // TODO: Don't convert to percentage, just check the threshold raw from the buffer // TODO: Skip parts of the spectrogram that cannot be human speech var percentage = (float)Math.Min(1d, Math.Sqrt(buffer[n]) * scaleFactor / ushort.MaxValue); if (percentage > 0.35) { verticalSamplesWithSound++; if (verticalSamplesWithSound > 4) { return(true); } } } } return(false); }
public override int GetData(float[] buffer) { var length = default(uint); switch (buffer.Length) { case 128: length = FFT256; break; case 256: length = FFT512; break; case 512: length = FFT1024; break; default: throw new NotImplementedException(); } foreach (var channelHandle in this.MixerChannelHandles) { return(BassMix.ChannelGetData(channelHandle, buffer, unchecked ((int)length))); } return(0); }
public virtual int GetData(float[] buffer) { foreach (var channelHandle in this.GetMixerChannelHandles()) { //Critical: BassMix.ChannelGetData will trigger an access violation in a random place if called concurrently with different buffer sizes. Yes this took a long time to work out. lock (ChannelDataSyncRoot) { return BassMix.ChannelGetData(channelHandle, buffer, buffer.Length); } } return 0; }
public virtual int GetData(float[] buffer, int fftSize) { var length = default(uint); switch (fftSize) { case BassFFT.FFT256: length = BassFFT.FFT256_MASK; break; case BassFFT.FFT512: length = BassFFT.FFT512_MASK; break; case BassFFT.FFT1024: length = BassFFT.FFT1024_MASK; break; case BassFFT.FFT2048: length = BassFFT.FFT2048_MASK; break; case BassFFT.FFT4096: length = BassFFT.FFT4096_MASK; break; case BassFFT.FFT8192: length = BassFFT.FFT8192_MASK; break; case BassFFT.FFT16384: length = BassFFT.FFT16384_MASK; break; case BassFFT.FFT32768: length = BassFFT.FFT32768_MASK; break; default: throw new NotImplementedException(); } foreach (var channelHandle in this.GetMixerChannelHandles()) { //Critical: BassMix.ChannelGetData will trigger an access violation in a random place if called concurrently with different buffer sizes. Yes this took a long time to work out. lock (ChannelDataSyncRoot) { return(BassMix.ChannelGetData(channelHandle, buffer, unchecked ((int)length))); } } return(0); }
private int DoWork(AudioVisualizer audioVisualizer, int visualChannel, Work queued, Rectangle clientRectangle) { var changes = 0; var todoWorkList = SplitAndMerge(queued); var buffer = new float[2048]; // Move back inside loop if behavior is odd foreach (var work in todoWorkList) { try { // TODO: Replace all this code with custom BASS_ChannelGetData instead, and paint a heatmap rather than a 3DVoicePrint _maxFrequencySpectrum = FFTFrequency2Index(audioVisualizer.GetFrequencyRange(), 4096, 44100); var clientXFrom = audioVisualizer.ByteIndexToClientX(work.From); var clientXTo = audioVisualizer.ByteIndexToClientX(work.To); var clientWidth = (int)(clientXTo - clientXFrom); if (clientWidth <= 0) { continue; } var bitmapWidth = clientWidth; var bitmapHeight = clientRectangle.Height; var bitmap = new DirectBitmap(bitmapWidth, bitmapHeight); work.Bitmap = bitmap; changes++; var clipRectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height); if (visualChannel == 0 || clipRectangle.Width <= 1 || clipRectangle.Height <= 1) { continue; } var bytesPerPixel = (double)(work.To - work.From) / bitmap.Width; var verticalSamplesPerPixel = (double)_maxFrequencySpectrum / clipRectangle.Height; var verticalSampleCount = _maxFrequencySpectrum + 1; const int scaleFactor = _scaleFactorSqr * ushort.MaxValue; for (var pos = 0; pos < clipRectangle.Width; pos++) { Bass.ChannelSetPosition(visualChannel, (long)Math.Floor(work.From + pos * bytesPerPixel)); var bufferResult = _isMixerUsed ? BassMix.ChannelGetData(visualChannel, buffer, (int)_maxFft) : Bass.ChannelGetData(visualChannel, buffer, (int)_maxFft); var y1 = 0; var highest = 0f; for (var index = 1; index < verticalSampleCount; ++index) { if (highest < buffer[index]) { // By only printing the pixel if we've passed the current pixel's frequencies, // and keeping the highest frequency of the pixel, we get a better representation. highest = Math.Max(0, buffer[index]); } // TODO: Should not need to divide each time, should be able to increment on each loop. var currentY = (int)(Math.Round(index / verticalSamplesPerPixel) - 1); if (currentY > y1) { var dbIndex = bitmap.GetStartOffset(pos, y1); // From near-purple blue (0.70) until red (0.0) in HSV hue wheel. var percentage = Math.Min(1d, Math.Sqrt(highest) * scaleFactor / ushort.MaxValue); var cacheIndex = (int)Math.Floor((_cachedColors.Length - 1) * percentage); var currentColor = _cachedColors[cacheIndex]; bitmap.Bits[dbIndex + 0] = currentColor.B; // B bitmap.Bits[dbIndex + 1] = currentColor.G; // G bitmap.Bits[dbIndex + 2] = currentColor.R; // R bitmap.Bits[dbIndex + 3] = 255; // A y1 = currentY; highest = 0; } } if (pos < clipRectangle.Width - 1) { for (var y = 0; y < clipRectangle.Height; y++) { var dbIndex = bitmap.GetStartOffset(pos + 1, y); bitmap.Bits[dbIndex + 0] = 0; bitmap.Bits[dbIndex + 1] = 0; bitmap.Bits[dbIndex + 2] = 0; bitmap.Bits[dbIndex + 3] = 255; } } } } catch (Exception ex) { Console.WriteLine(ex.Message); } } return(changes); }
public int ChannelGetData(IBassAudioChannel channel, float[] buffer, int length) => BassMix.ChannelGetData(channel.Handle, buffer, length);