Пример #1
0
        /// <summary>
        /// This method takes the left channel and right channel wave raw data and analyses it to get
        /// the maximum and minimum values in the float structure. It returns a data structure named
        /// WaveDataMinMax (see class description for more information). Negative values can be converted to
        /// positive values before min and max comparaison. Set this parameter to true for output meters and
        /// false for wave form display controls.
        /// </summary>
        /// <param name="waveDataLeft">Raw wave data (left channel)</param>
        /// <param name="waveDataRight">Raw wave data (right channel)</param>
        /// <param name="convertNegativeToPositive">Convert negative values to positive values (ex: true when used for output meters, 
        /// false when used with wave form display controls (since the negative value is used to draw the bottom end of the waveform).</param>
        /// <returns>WaveDataMinMax data structure</returns>
        public static WaveDataMinMax GetMinMaxFromWaveData(float[] waveDataLeft, float[] waveDataRight, bool convertNegativeToPositive)
        {
            // Create default data
            WaveDataMinMax data = new WaveDataMinMax();

            // Loop through values to get min/max
            for (int i = 0; i < waveDataLeft.Length; i++)
            {
                // Set values to compare
                float left = waveDataLeft[i];
                float right = waveDataRight[i];

                // Do we have to convert values before comparaison?
                if (convertNegativeToPositive)
                {
                    // Compare values, if negative then remove negative sign
                    if (left < 0)
                    {
                        left = -left;
                    }
                    if (right < 0)
                    {
                        right = -right;
                    }
                }

                // Calculate min/max for left channel
                if (left < data.leftMin)
                {
                    data.leftMin = left;
                }
                if (left > data.leftMax)
                {
                    data.leftMax = left;
                }

                // Calculate min/max for right channel
                if (right < data.rightMin)
                {
                    data.rightMin = right;
                }
                if (right > data.rightMax)
                {
                    data.rightMax = right;
                }

                // Calculate min/max mixing both channels
                if (left < data.mixMin)
                {
                    data.mixMin = left;
                }
                if (right < data.mixMin)
                {
                    data.mixMin = right;
                }
                if (left > data.mixMax)
                {
                    data.mixMax = left;
                }
                if (right > data.mixMax)
                {
                    data.mixMax = right;
                }
            }

            return data;
        }
Пример #2
0
        /// <summary>
        /// Reads a peak file and returns a min/max peak list.
        /// </summary>
        /// <param name="peakFilePath">Peak file path</param>
        /// <returns>List of min/max peaks</returns>
        public List<WaveDataMinMax> ReadPeakFile(string peakFilePath)
        {
            // Declare variables 
            FileStream fileStream = null;
            GZipStream gzipStream = null;
            BinaryReader binaryReader = null;
            List<WaveDataMinMax> listMinMax = new List<WaveDataMinMax>();
            string fileHeader = null;
            long audioFileLength = 0;
            int chunkSize = 0;
            int numberOfBlocks = 0;
            int currentBlock = 0;

            try
            {
                // Create file stream
                fileStream = new FileStream(peakFilePath, FileMode.Open, FileAccess.Read);
                binaryReader = new BinaryReader(fileStream);
                gzipStream = new GZipStream(fileStream, CompressionMode.Decompress);

                // Read file header (34 characters) 
                // Ex: Sessions Peak File (version# 1.00)                
                fileHeader = new string(binaryReader.ReadChars(35));

                // Extract version and validate
                string version = fileHeader.Substring(fileHeader.Length - 5, 4);
                if (version != _version)
                    throw new PeakFileFormatIncompatibleException("Error: The peak file format is not compatible. Expecting version " + _version + " instead of version " + version + ".", null);

                // Read audio file length
                audioFileLength = binaryReader.ReadInt64();
                chunkSize = binaryReader.ReadInt32();
                numberOfBlocks = binaryReader.ReadInt32();

                // Loop through data
                while (binaryReader.PeekChar() != -1)
                {
                    currentBlock++;

                    // Read peak information and add to list
                    WaveDataMinMax peak = new WaveDataMinMax();
                    peak.leftMin = (float)binaryReader.ReadDouble();
                    peak.leftMax = (float)binaryReader.ReadDouble();
                    peak.rightMin = (float)binaryReader.ReadDouble();
                    peak.rightMax = (float)binaryReader.ReadDouble();
                    peak.mixMin = (float)binaryReader.ReadDouble();
                    peak.mixMax = (float)binaryReader.ReadDouble();
                    listMinMax.Add(peak);                    
                }

                // Validate number of blocks read
                if (currentBlock < numberOfBlocks - 1)
                    throw new PeakFileCorruptedException("Error: The peak file is corrupted (the number of blocks didn't match)!", null);
            }
            catch (Exception ex)
            {
                throw new PeakFileCorruptedException("Error: The peak file is corrupted!", ex);
            }
            finally
            {
                gzipStream.Close();
                binaryReader.Close();
                fileStream.Close();
            }

            return listMinMax;
        }
Пример #3
0
        private void RequestBitmapInternal(Object stateInfo)
        {
            // Use this instead of a task, this guarantees to execute in another thread
            //Console.WriteLine("WaveFormRenderingService - RequestBitmap - boundsBitmap: {0} boundsWaveForm: {1} zoom: {2}", boundsBitmap, boundsWaveForm, zoom);
            var request = stateInfo as WaveFormBitmapRequest;
            IMemoryGraphicsContext context;
            try
            {
                //Console.WriteLine("WaveFormRenderingService - Creating image cache...");
                context = _memoryGraphicsContextFactory.CreateMemoryGraphicsContext(request.BoundsBitmap.Width, request.BoundsBitmap.Height);
                if (context == null)
                {
                    Console.WriteLine("Error initializing image cache context!");
					return;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error while creating image cache context: " + ex.Message);
                return;
            }

			IBasicImage imageCache;
			try
			{
			    float x1 = 0;
                float x2 = 0;
                float leftMin = 0;
                float leftMax = 0;
                float rightMin = 0;
                float rightMax = 0;
                float mixMin = 0;
                float mixMax = 0;
                int historyIndex = 0;
                int historyCount = _waveDataCache.Count;
                float lineWidth = 0;
                int nHistoryItemsPerLine = 0;
                const float desiredLineWidth = 0.5f;
                WaveDataMinMax[] subset = null;

                // Find out how many samples are represented by each line of the wave form, depending on its width.
                // For example, if the history has 45000 items, and the control has a width of 1000px, 45 items will need to be averaged by line.
                float lineWidthPerHistoryItem = request.BoundsWaveForm.Width / (float)historyCount;

                // Check if the line width is below the desired line width
                if (lineWidthPerHistoryItem < desiredLineWidth)
                {
                    // Try to get a line width around 0.5f so the precision is good enough and no artifacts will be shown.
                    while (lineWidth < desiredLineWidth)
                    {
                        // Increment the number of history items per line
                        //Console.WriteLine("Determining line width (lineWidth: " + lineWidth.ToString() + " desiredLineWidth: " + desiredLineWidth.ToString() + " nHistoryItemsPerLine: " + nHistoryItemsPerLine.ToString() + " lineWidthPerHistoryItem: " + lineWidthPerHistoryItem.ToString());
                        nHistoryItemsPerLine++;
                        lineWidth += lineWidthPerHistoryItem;
                    }
                    nHistoryItemsPerLine--;
                    lineWidth -= lineWidthPerHistoryItem;
                }
                else
                {
                    // The lines are larger than 0.5 pixels.
                    lineWidth = lineWidthPerHistoryItem;
                    nHistoryItemsPerLine = 1;
                }

                float heightToRenderLine = 0;
                if (request.DisplayType == WaveFormDisplayType.Stereo)
                    heightToRenderLine = (request.BoundsWaveForm.Height / 4);
                else
                    heightToRenderLine = (request.BoundsWaveForm.Height / 2);

                context.DrawRectangle(new BasicRectangle(0, 0, request.BoundsBitmap.Width + 2, request.BoundsBitmap.Height), _brushBackground, _penTransparent);

                // The pen cannot be cached between refreshes because the line width changes every time the width changes
                //context.SetLineWidth(0.2f);
                var penWaveForm = new BasicPen(new BasicBrush(_colorWaveForm), lineWidth);
                context.SetPen(penWaveForm);

                float startLine = ((int)Math.Floor(request.BoundsBitmap.X / lineWidth)) * lineWidth;
                historyIndex = (int) ((startLine / lineWidth) * nHistoryItemsPerLine);

                //Console.WriteLine("WaveFormRenderingService - startLine: {0} boundsWaveForm.Width: {1} nHistoryItemsPerLine: {2} historyIndex: {3}", startLine, boundsWaveForm.Width, nHistoryItemsPerLine, historyIndex);
                //List<float> roundValues = new List<float>();
                float lastLine = startLine + request.BoundsBitmap.Width;
                if (request.BoundsWaveForm.Width - request.BoundsBitmap.X < request.BoundsBitmap.Width)
                    lastLine = request.BoundsWaveForm.Width;

                //context.DrawText(string.Format("{0:0.0}", startLine), new BasicPoint(1, request.BoundsBitmap.Height - 10), new BasicColor(255, 255, 255), "Roboto Bold", 10 * context.Density);
                //context.DrawText(string.Format("{0:0.0}", lastLine), new BasicPoint(1, request.BoundsBitmap.Height - 22), new BasicColor(255, 255, 255), "Roboto Bold", 10 * context.Density);
                //context.DrawText(string.Format("{0:0.0}", request.BoundsBitmap.X), new BasicPoint(1, request.BoundsBitmap.Height - 34), new BasicColor(255, 255, 255), "Roboto Bold", 10 * context.Density);
                //context.DrawText(string.Format("{0:0.0}", request.BoundsWaveForm.Width), new BasicPoint(1, request.BoundsBitmap.Height - 46), new BasicColor(255, 255, 255), "Roboto Bold", 10 * context.Density);
                //context.DrawText(string.Format("{0}", request.BoundsBitmap.X), new BasicPoint(1, request.BoundsBitmap.Height - 20), new BasicColor(255, 255, 255), "Roboto Bold", 10 * context.Density);
                //context.DrawText(string.Format("{0:0.0}", request.Zoom), new BasicPoint(1, request.BoundsBitmap.Height - 10), new BasicColor(255, 255, 255), "Roboto Bold", 10 * context.Density);

                for (float i = startLine; i < lastLine; i += lineWidth)
                {
                    #if MACOSX
                    // On Mac, the pen needs to be set every time we draw or the color might change to black randomly (weird?)
                    context.SetPen(penWaveForm);
                    #endif

                    // COMMENTED possible solution for varying line widths rendering problem
                    // Round to 0.5
                    //i = (float)Math.Round(i * 2) / 2;
                    //float iRound = (float)Math.Round(i);
                    //float iRound = (float)Math.Round(i * 2) / 2;
                    //float iRound = (float)Math.Round(i * 4) / 4;

                    //                        // If this value has already been drawn, skip it (this happens because of the rounding, and this fixes a visual bug)
                    //                        if(roundValues.Contains(iRound))
                    //                        {
                    //                            // Increment the history index; pad the last values if the count is about to exceed
                    //                            if (historyIndex < historyCount - 1)
                    //                                historyIndex += nHistoryItemsPerLine;                         
                    //                            continue;
                    //                        }
                    //                        else
                    //                        {
                    //                            roundValues.Add(iRound);
                    //                        }

                    // Determine the maximum height of a line (+/-)
                    //Console.WriteLine("WaveForm - Rendering " + i.ToString() + " (rnd=" + iRound.ToString() + ") on " + widthAvailable.ToString());

                    // Determine x position
                    //                        x1 = iRound; //i;
                    //                        x2 = iRound; //i;
                    x1 = i - startLine;
                    x2 = i - startLine;
                    if (nHistoryItemsPerLine > 1)
                    {
                        if (historyIndex + nHistoryItemsPerLine > historyCount)
                        {
                            // Create subset with remaining data
                            subset = new WaveDataMinMax[historyCount - historyIndex];
                            _waveDataCache.CopyTo(historyIndex, subset, 0, historyCount - historyIndex);
                        }
                        else
                        {
                            subset = new WaveDataMinMax[nHistoryItemsPerLine];
                            _waveDataCache.CopyTo(historyIndex, subset, 0, nHistoryItemsPerLine);
                        }

                        leftMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Left);
                        leftMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Left);
                        rightMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Right);
                        rightMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Right);
                        mixMin = AudioTools.GetMinPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Mix);
                        mixMax = AudioTools.GetMaxPeakFromWaveDataMaxHistory(subset.ToList(), nHistoryItemsPerLine, ChannelType.Mix);
                    }
                    else
                    {
                        leftMin = _waveDataCache[historyIndex].leftMin;
                        leftMax = _waveDataCache[historyIndex].leftMax;
                        rightMin = _waveDataCache[historyIndex].rightMin;
                        rightMax = _waveDataCache[historyIndex].rightMax;
                        mixMin = _waveDataCache[historyIndex].mixMin;
                        mixMax = _waveDataCache[historyIndex].mixMax;
                    }

                    float leftMaxHeight = leftMax * heightToRenderLine;
                    float leftMinHeight = leftMin * heightToRenderLine;
                    float rightMaxHeight = rightMax * heightToRenderLine;
                    float rightMinHeight = rightMin * heightToRenderLine;
                    float mixMaxHeight = mixMax * heightToRenderLine;
                    float mixMinHeight = mixMin * heightToRenderLine;

                    //Console.WriteLine("WaveFormRenderingService - line: {0} x1: {1} x2: {2} historyIndex: {3} historyCount: {4} width: {5}", i, x1, x2, historyIndex, historyCount, boundsWaveForm.Width);
                    if (request.DisplayType == WaveFormDisplayType.LeftChannel ||
                        request.DisplayType == WaveFormDisplayType.RightChannel ||
                        request.DisplayType == WaveFormDisplayType.Mix)
                    {
                        // Calculate min/max line height
                        float minLineHeight = 0;
                        float maxLineHeight = 0;

                        // Set mib/max
                        if (request.DisplayType == WaveFormDisplayType.LeftChannel)
                        {
                            minLineHeight = leftMinHeight;
                            maxLineHeight = leftMaxHeight;
                        }
                        else if (request.DisplayType == WaveFormDisplayType.RightChannel)
                        {
                            minLineHeight = rightMinHeight;
                            maxLineHeight = rightMaxHeight;
                        }
                        else if (request.DisplayType == WaveFormDisplayType.Mix)
                        {
                            minLineHeight = mixMinHeight;
                            maxLineHeight = mixMaxHeight;
                        }

                        // Positive Max Value - Draw positive value (y: middle to top)                   
						context.StrokeLine(new BasicPoint(x1, heightToRenderLine), new BasicPoint(x2, heightToRenderLine - maxLineHeight));
                        // Negative Max Value - Draw negative value (y: middle to height)
						context.StrokeLine(new BasicPoint(x1, heightToRenderLine), new BasicPoint(x2, heightToRenderLine + (-minLineHeight)));
                    }
                    else if (request.DisplayType == WaveFormDisplayType.Stereo)
                    {
                        // LEFT Channel - Positive Max Value - Draw positive value (y: middle to top)
                        context.StrokeLine(new BasicPoint(x1, heightToRenderLine), new BasicPoint(x2, heightToRenderLine - leftMaxHeight));
                        // LEFT Channel - Negative Max Value - Draw negative value (y: middle to height)
                        context.StrokeLine(new BasicPoint(x1, heightToRenderLine), new BasicPoint(x2, heightToRenderLine + (-leftMinHeight)));
                        // RIGHT Channel - Positive Max Value (Multiply by 3 to get the new center line for right channel) - Draw positive value (y: middle to top)
                        context.StrokeLine(new BasicPoint(x1, (heightToRenderLine * 3)), new BasicPoint(x2, (heightToRenderLine * 3) - rightMaxHeight));
                        // RIGHT Channel - Negative Max Value - Draw negative value (y: middle to height)
                        context.StrokeLine(new BasicPoint(x1, (heightToRenderLine * 3)), new BasicPoint(x2, (heightToRenderLine * 3) + (-rightMinHeight)));
                    }

                    // Increment the history index; pad the last values if the count is about to exceed
                    if (historyIndex < historyCount - 1)
                        historyIndex += nHistoryItemsPerLine;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error while creating image cache: " + ex.Message);
            }
            finally
            {
                // Get image from context (at this point, we are sure the image context has been initialized properly)
				//Console.WriteLine("WaveFormRenderingService - Rendering image to memory...");
                context.Close();
                imageCache = context.RenderToImageInMemory();
            }

			//Console.WriteLine("WaveFormRenderingService - Created image successfully.");
            //stopwatch.Stop();
            //Console.WriteLine("WaveFormRenderingService - Created image successfully in {0} ms.", stopwatch.ElapsedMilliseconds);
			OnGenerateWaveFormBitmapEnded(new GenerateWaveFormEventArgs()
			{
				//AudioFilePath = audioFile.FilePath,
                OffsetX = request.BoundsBitmap.X,
				Zoom = request.Zoom,
                Width = context.BoundsWidth,
				DisplayType = request.DisplayType,
				Image = imageCache
			});
        }