Example #1
0
        /// <summary>
        /// Get a spectrogram of the signal specified at the input
        /// </summary>
        /// <param name="spectrogram">Signal</param>
        /// <param name="width">Width of the image</param>
        /// <param name="height">Height of the image</param>
        /// <param name="milliseconds">Time in ms</param>
        /// <param name="sampleRate">Sample rate in hz</param>
        /// <param name="colorPalette">Specify to color palette</param>
        /// <param name="doLogScale">log scale or not?</param>
        /// <param name="logFrequenciesIndex">log frequency index array</param>
        /// <param name="logFrequencies">log frequency array</param>
        /// <remarks>
        ///   X axis - time
        ///   Y axis - frequency
        ///   Color - magnitude level of corresponding band value of the signal
        /// <returns>Spectral image of the signal</returns>
        public static Bitmap GetSpectrogramImage(float[][] spectrogram, int width, int height, double milliseconds, double sampleRate, ColorUtils.ColorPaletteType colorPalette, bool doLogScale, int[] logFrequenciesIndex, float[] logFrequencies)
        {
            #if SAFE
            if (width < 0)
                throw new ArgumentException("width should be bigger than 0");
            if (height < 0)
                throw new ArgumentException("height should be bigger than 0");
            #endif

            bool drawLabels = true;
            float minDb = -90.0f; // -80.0f also works good
            float maxDb = 10.0f; // with the current color palettes 10.0f works well

            // Basic constants
            int TOTAL_HEIGHT = height;    // Height of graph
            int TOTAL_WIDTH = width;      // Width of graph

            int TOP = 40;                    // Top of graph
            int LEFT = 60;                   // Left edge of graph
            int HEIGHT = height-2*TOP;		// Height of graph
            int WIDTH = width-2*LEFT;     	// Width of graph
            string LABEL_X = "Time (ms)"; 		// Label for X axis
            string LABEL_Y = "Frequency (Hz)";  // Label for Y axis

            float MAX_FREQ = (float) sampleRate / 2;	// Maximum frequency (Hz) on vertical axis.
            float MIN_FREQ = 27.5f;        	// Minimum frequency (Hz) on vertical axis.
            float FREQ_STEP = 1000;        	// Interval between ticks (dB) on vertical axis.

            // if the max frequency gets lower than ... lower the frequency step
            if (MAX_FREQ < 20000) {
                FREQ_STEP = (float) MathUtils.GetNicerNumber(MAX_FREQ / 20);
            }

            // Derived constants
            int BOTTOM = TOTAL_HEIGHT-TOP;                   			// Bottom of graph
            float FREQTOPIXEL = (float) HEIGHT/(MAX_FREQ-MIN_FREQ);    	// Pixels/Hz

            float MIN_TIME = 0.0f;
            float MAX_TIME = (float) milliseconds;
            if (MAX_TIME == 0) MAX_TIME = 1000;

            // Interval between ticks (time) on horizontal axis.
            float TIME_STEP = (float) MathUtils.GetNicerNumber(MAX_TIME / 20);
            float TIMETOPIXEL = (float) WIDTH/(MAX_TIME-MIN_TIME); 	// Pixels/second

            // Colors
            // black, gray, white style
            Color lineColor = ColorTranslator.FromHtml("#BFBFBF");
            Color middleLineColor = ColorTranslator.FromHtml("#BFBFBF");
            Color labelColor = ColorTranslator.FromHtml("#FFFFFF");
            Color tickColor = ColorTranslator.FromHtml("#BFBFBF");
            Color fillOuterColor = ColorTranslator.FromHtml("#000000");
            Color fillColor = ColorTranslator.FromHtml("#000000");

            Bitmap fullImage = new Bitmap(TOTAL_WIDTH, TOTAL_HEIGHT);
            Graphics g = Graphics.FromImage(fullImage);

            Pen linePen = new Pen(lineColor, 0.5f);
            Pen middleLinePen = new Pen(middleLineColor, 0.5f);
            Pen labelPen = new Pen(labelColor, 1);
            Pen tickPen = new Pen(tickColor, 1);

            // Draw a rectangular box marking the boundaries of the graph
            Rectangle rectOuter = new Rectangle(0, 0, TOTAL_WIDTH, TOTAL_HEIGHT);
            Brush fillBrushOuter = new SolidBrush(fillOuterColor);
            g.FillRectangle(fillBrushOuter, rectOuter);

            // Create rectangle.
            Rectangle rect = new Rectangle(LEFT, TOP, WIDTH, HEIGHT);
            Brush fillBrush = new SolidBrush(fillColor);
            g.FillRectangle(fillBrush, rect);
            g.DrawRectangle(linePen, rect);

            // Label for horizontal axis
            Font drawLabelFont = new Font("Arial", 8);
            SolidBrush drawLabelBrush = new SolidBrush(labelPen.Color);
            if (drawLabels) {
                SizeF drawLabelTextSize = g.MeasureString(LABEL_X, drawLabelFont);
                g.DrawString(LABEL_X, drawLabelFont, drawLabelBrush, (TOTAL_WIDTH/2) - (drawLabelTextSize.Width/2), TOTAL_HEIGHT - drawLabelFont.GetHeight(g) - 5);
            }

            float y = 0;
            float yMiddle = 0;
            float x = 0;
            float xMiddle = 0;

            if (!doLogScale) {
                // LINEAR SCALE

                // Tick marks on the vertical axis
                for ( float freqTick = MIN_FREQ; freqTick <= MAX_FREQ; freqTick += FREQ_STEP )
                {
                    // draw horozontal main line
                    y = BOTTOM - FREQTOPIXEL*(freqTick-MIN_FREQ);
                    if (y < BOTTOM && y > TOP+1) {
                        g.DrawLine(linePen, LEFT-2, y, LEFT+WIDTH+2, y);
                    }

                    // draw horozontal middle line (between the main lines)
                    yMiddle = y-(FREQTOPIXEL*FREQ_STEP)/2;
                    if (yMiddle > TOP && yMiddle < HEIGHT+TOP) {
                        g.DrawLine(middleLinePen, LEFT, yMiddle, LEFT+WIDTH, yMiddle);
                    }

                    if ( freqTick != MAX_FREQ )
                    {
                        // Numbers on the tick marks
                        Font drawFont = new Font("Arial", 8);
                        SolidBrush drawBrush = new SolidBrush(tickPen.Color);

                        // left
                        g.DrawString(MathUtils.FormatNumber((int) freqTick), drawFont, drawBrush, LEFT - 33, y - drawFont.GetHeight(g)/2);

                        // right
                        g.DrawString(MathUtils.FormatNumber((int) freqTick), drawFont, drawBrush, WIDTH + LEFT + 4, y - drawFont.GetHeight(g)/2);
                    }
                }
            } else {
                // LOG SCALE
                for (int i = 0; i < logFrequencies.Length; i+=20)
                {
                    float freqTick = logFrequencies[i];
                    y = BOTTOM - i;

                    // draw horozontal main line
                    if (y < BOTTOM && y > TOP+1) {
                        g.DrawLine(linePen, LEFT-2, y, LEFT+WIDTH+2, y);
                    }

                    // Numbers on the tick marks
                    Font drawFont = new Font("Arial", 8);
                    SolidBrush drawBrush = new SolidBrush(tickPen.Color);

                    // left
                    g.DrawString(MathUtils.FormatNumber((int) freqTick), drawFont, drawBrush, LEFT - 33, y - drawFont.GetHeight(g)/2);

                    // right
                    g.DrawString(MathUtils.FormatNumber((int) freqTick), drawFont, drawBrush, WIDTH + LEFT + 4, y - drawFont.GetHeight(g)/2);
                }
            }

            if (drawLabels) {
                // Label for vertical axis
                StringFormat format = new StringFormat();
                format.Alignment = StringAlignment.Center;
                g.TranslateTransform(g.VisibleClipBounds.Width, 0);
                g.RotateTransform(270);
                g.DrawString(LABEL_Y, drawLabelFont, drawLabelBrush, -(TOTAL_HEIGHT/2), -TOTAL_WIDTH + 5, format);
                g.ResetTransform();
            }

            // Tick marks on the horizontal axis
            for ( float timeTick = MIN_TIME; timeTick <= MAX_TIME; timeTick += TIME_STEP )
            {
                // draw vertical main line
                x = LEFT + TIMETOPIXEL*(timeTick-MIN_TIME);
                if (x > LEFT  && x < WIDTH) {
                    g.DrawLine(linePen, x, BOTTOM+2, x, TOP-2);
                }

                // draw vertical middle line (between the main lines)
                xMiddle = x + TIMETOPIXEL*TIME_STEP/2;
                if (xMiddle < WIDTH+LEFT) {
                    g.DrawLine(middleLinePen, xMiddle, BOTTOM, xMiddle, TOP);
                }

                if ( timeTick != MIN_TIME && timeTick != MAX_TIME )
                {
                    // Numbers on the tick marks
                    Font drawFont = new Font("Arial", 8);
                    SolidBrush drawBrush = new SolidBrush(tickPen.Color);
                    SizeF drawTimeTickTextSize = g.MeasureString("" + timeTick, drawFont);

                    // top
                    g.DrawString("" + timeTick, drawFont, drawBrush, x-(drawTimeTickTextSize.Width/2), TOP - 15);

                    // bottom
                    g.DrawString("" + timeTick, drawFont, drawBrush, x-(drawTimeTickTextSize.Width/2), BOTTOM + 2);
                }
            }

            // draw spectrogram
            Bitmap spectrogramImage = new Bitmap(WIDTH, HEIGHT);

            // calculate min and max
            double max = spectrogram.Max((b) => b.Max((v) => Math.Abs(v)));
            double min = spectrogram.Min((b) => b.Min((v) => Math.Abs(v)));

            int numberOfSamplesX = spectrogram.Length; 	// time
            int numberOfSamplesY = spectrogram[0].Length; 	// hz

            double deltaX = (double) (WIDTH - 1)/(numberOfSamplesX); 	// By how much the image will move to the left
            double deltaY = (double) (HEIGHT- 1)/(numberOfSamplesY); 	// By how much the image will move upward

            int prevX = 0;
            Color prevColor = Color.Black;
            for (int i = 0; i < numberOfSamplesX; i++)
            {
                double xCoord = i*deltaX;
                if ((int) xCoord == prevX) continue;
                for (int j = 0; j < numberOfSamplesY; j++)
                {
                    float amplitude = spectrogram[i][j];
                    Color colorbw = Color.Black;
                    if (amplitude > 0) {
                        float dB = MathUtils.AmplitudeToDecibel(amplitude, minDb, maxDb);
                        int colorval = (int) MathUtils.ConvertAndMainainRatio(dB, minDb, maxDb, 0, 255); // 255 is full brightness, and good for REW colors (for SOX 220 is good, and for PHOTOSOUNDER 245 seems good)
                        colorbw = Color.FromArgb(colorval, colorval, colorval);
                        //colorbw = ValueToBlackWhiteColor(amplitude, max*0.010);
                        prevColor = colorbw;
                    } else {
                        colorbw = prevColor;
                    }
                    spectrogramImage.SetPixel((int) xCoord + 1, HEIGHT - (int) (deltaY*j) - 1, colorbw);
                }
                prevX = (int) xCoord;
            }

            if (colorPalette != ColorUtils.ColorPaletteType.BLACK_AND_WHITE) {
                spectrogramImage = ColorUtils.Colorize(spectrogramImage, 255, colorPalette);
            }

            // add the spectrogram to the full image
            g.DrawImage(spectrogramImage, LEFT, TOP);

            return fullImage;
        }
Example #2
0
        /// <summary>
        /// Utility method to return spectrogram image using audio data
        /// </summary>
        /// <param name="audioData"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="sampleRate"></param>
        /// <param name="fftWindowsSize"></param>
        /// <param name="fftOverlap"></param>
        /// <param name="colorPalette"></param>
        /// <param name="doLogScale"></param>
        /// <returns>Spectrogram image</returns>
        public static Bitmap GetSpectrogramImage(float[] audioData, int width, int height, double sampleRate, int fftWindowsSize, int fftOverlap, ColorUtils.ColorPaletteType colorPalette, bool doLogScale)
        {
            float[][] spectrogram;
            double minFrequency = 27.5;
            double maxFrequency = sampleRate / 2;
            int logBins = height - 2*40; // the margins used
            int[] logFrequenciesIndex = new int[1];
            float[] logFrequencies = new float[1];

            // find the time
            int numberOfSamples = audioData.Length;
            double seconds = numberOfSamples / sampleRate;

            if (!doLogScale) {
                spectrogram = CreateSpectrogramLomont(audioData, fftWindowsSize, fftOverlap);
            } else {
                // calculate the log frequency index table
                GetLogFrequenciesIndex(sampleRate, minFrequency, maxFrequency, logBins, fftWindowsSize, LogBase, out logFrequenciesIndex, out logFrequencies);
                spectrogram = CreateLogSpectrogramLomont(audioData, fftWindowsSize, fftOverlap, logBins, logFrequenciesIndex, logFrequencies);
            }

            return GetSpectrogramImage(spectrogram, width, height, seconds*1000, sampleRate, colorPalette, doLogScale, logFrequenciesIndex, logFrequencies);
        }