From b354cf88c2442848057fc0cac7ec2e0063001083 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 17 Nov 2024 15:38:00 +0100 Subject: [PATCH 01/21] adding spectrum --- audiometer.lpi | 7 +- audiometer.lpr | 2 +- src/mainfrm.lfm | 649 ++++++++++++++++++++++++++++++++++------------- src/mainfrm.pas | 179 ++++++++++++- src/soundwav.pas | 48 +++- 5 files changed, 694 insertions(+), 191 deletions(-) diff --git a/audiometer.lpi b/audiometer.lpi index 945b005..1711ea8 100644 --- a/audiometer.lpi +++ b/audiometer.lpi @@ -75,6 +75,12 @@ + + + + + + @@ -132,7 +138,6 @@ - diff --git a/audiometer.lpr b/audiometer.lpr index 9b9cb66..6f9c1a4 100644 --- a/audiometer.lpr +++ b/audiometer.lpr @@ -1,7 +1,7 @@ { Description: AudioMeter application. - Copyright (C) 2020-2023 Melchiorre Caruso + Copyright (C) 2020-2024 Melchiorre Caruso This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index b38ebfd..aab8b18 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -1,30 +1,30 @@ object audiofrm: Taudiofrm - Left = 826 - Height = 500 - Top = 409 + Left = 356 + Height = 571 + Top = 111 Width = 700 - Caption = 'AudioMeter 0.4.4 - Dynamic Range Meter' - ClientHeight = 500 + Caption = 'AudioMeter 0.4.6 - Dynamic Range Meter' + ClientHeight = 571 ClientWidth = 700 Color = clGray Constraints.MinHeight = 440 Constraints.MinWidth = 700 Font.Color = clWhite Font.Style = [fsBold] - Position = poDesktopCenter - LCLVersion = '3.99.0.0' OnCloseQuery = FormCloseQuery OnCreate = FormCreate OnDestroy = FormDestroy OnResize = FormResize + Position = poDesktopCenter + LCLVersion = '3.6.0.0' object audio: TLabel AnchorSideLeft.Control = Owner AnchorSideTop.Control = Owner AnchorSideBottom.Side = asrBottom Left = 26 - Height = 19 + Height = 15 Top = 10 - Width = 40 + Width = 32 BorderSpacing.Left = 26 BorderSpacing.Top = 10 Caption = 'Audio' @@ -35,91 +35,21 @@ object audiofrm: Taudiofrm ParentFont = False Transparent = False end - object dbchart: TChart - AnchorSideLeft.Control = Owner - AnchorSideTop.Control = DetailsPanel - AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = Owner - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = Owner - AnchorSideBottom.Side = asrBottom - Left = 3 - Height = 310 - Top = 185 - Width = 694 - AllowPanning = False - AllowZoom = False - AxisList = < - item - Grid.Visible = False - Marks.LabelFont.Color = clWhite - Marks.LabelFont.Height = -13 - Marks.LabelFont.Name = 'Sans' - Marks.LabelBrush.Style = bsClear - Minors = <> - Range.Max = 100 - Range.UseMax = True - Range.UseMin = True - Title.LabelFont.Color = clWhite - Title.LabelFont.Height = -13 - Title.LabelFont.Name = 'Sans' - Title.LabelFont.Orientation = 900 - Title.LabelFont.Style = [fsBold] - Title.Visible = True - Title.Caption = 'dB' - Title.LabelBrush.Style = bsClear - end - item - Grid.Visible = False - Alignment = calBottom - Arrow.Visible = True - Marks.LabelFont.Color = clWhite - Marks.LabelBrush.Style = bsClear - Minors = <> - Range.Max = 25 - Range.UseMax = True - Range.UseMin = True - Title.LabelFont.Color = clWhite - Title.LabelFont.Height = -13 - Title.LabelFont.Name = 'Sans' - Title.LabelFont.Style = [fsBold] - Title.Visible = True - Title.Caption = 'Block-Num' - Title.LabelBrush.Style = bsClear - end> - BackColor = clBlack - Foot.Brush.Color = clBtnFace - Foot.Font.Color = clBlue - Title.Brush.Color = clBtnFace - Title.Font.Color = clBlue - Title.Text.Strings = ( - '' - ) - Anchors = [akTop, akLeft, akRight, akBottom] - BorderSpacing.Left = 3 - BorderSpacing.Top = 25 - BorderSpacing.Right = 3 - BorderSpacing.Bottom = 5 - Color = clBlack - object rmseries: TBarSeries - BarBrush.Color = clYellow - Source = rms - end - object peakseries: TBarSeries - BarBrush.Color = clRed - Source = peak - end - end object btnfile: TImage AnchorSideTop.Control = btnfolder AnchorSideRight.Control = btnfolder AnchorSideRight.Side = asrBottom Left = 611 Height = 32 - Top = 34 + Top = 30 Width = 32 Anchors = [akTop, akRight] BorderSpacing.Right = 45 + OnClick = btnfileClick + OnMouseDown = btnfileMouseDown + OnMouseLeave = btnfileMouseLeave + OnMouseMove = btnfileMouseMove + OnMouseUp = btnfileMouseUp Picture.Data = { 1754506F727461626C654E6574776F726B47726170686963C102000089504E47 0D0A1A0A0000000D49484452000000200000001E08060000004D0A1C29000000 @@ -149,11 +79,6 @@ object audiofrm: Taudiofrm StretchOutEnabled = False StretchInEnabled = False Transparent = True - OnClick = btnfileClick - OnMouseDown = btnfileMouseDown - OnMouseLeave = btnfileMouseLeave - OnMouseMove = btnfileMouseMove - OnMouseUp = btnfileMouseUp end object btnfolder: TImage AnchorSideTop.Control = audio @@ -162,11 +87,16 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom Left = 656 Height = 32 - Top = 34 + Top = 30 Width = 32 Anchors = [akTop, akRight] BorderSpacing.Top = 5 BorderSpacing.Right = 12 + OnClick = btnfolderclick + OnMouseDown = btnfolderMouseDown + OnMouseLeave = btnfoldermouseleave + OnMouseMove = btnfoldermousemove + OnMouseUp = btnfoldermouseup Picture.Data = { 1754506F727461626C654E6574776F726B47726170686963E701000089504E47 0D0A1A0A0000000D494844520000002000000015080600000027CDECEA000000 @@ -190,50 +120,45 @@ object audiofrm: Taudiofrm StretchOutEnabled = False StretchInEnabled = False Transparent = True - OnClick = btnfolderclick - OnMouseDown = btnfolderMouseDown - OnMouseLeave = btnfoldermouseleave - OnMouseMove = btnfoldermousemove - OnMouseUp = btnfoldermouseup end object DetailsPanel: TPanel AnchorSideLeft.Control = audio AnchorSideTop.Control = audio AnchorSideTop.Side = asrBottom Left = 26 - Height = 126 - Top = 34 - Width = 141 + Height = 106 + Top = 30 + Width = 119 AutoSize = True BorderSpacing.Top = 5 BevelOuter = bvNone - ClientHeight = 126 - ClientWidth = 141 + ClientHeight = 106 + ClientWidth = 119 Color = clBlack ParentBackground = False ParentColor = False - TabOrder = 1 + TabOrder = 0 object BitsPanel: TPanel AnchorSideLeft.Control = DetailsPanel AnchorSideTop.Control = Bevel1 AnchorSideTop.Side = asrBottom Left = 0 - Height = 19 + Height = 15 Top = 6 - Width = 141 + Width = 119 AutoSize = True BorderSpacing.Top = 5 BevelOuter = bvNone - ClientHeight = 19 - ClientWidth = 141 + ClientHeight = 15 + ClientWidth = 119 ParentBackground = False TabOrder = 0 object bit8: TLabel AnchorSideLeft.Control = BitsPanel Left = 4 - Height = 19 + Height = 15 Top = 0 - Width = 34 + Width = 27 BorderSpacing.Left = 4 Caption = '8-bit' Color = clBlack @@ -249,10 +174,10 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = bit8 AnchorSideBottom.Control = bit8 AnchorSideBottom.Side = asrBottom - Left = 46 - Height = 19 + Left = 39 + Height = 15 Top = 0 - Width = 41 + Width = 34 Anchors = [akTop, akLeft, akBottom] BorderSpacing.Left = 8 Caption = '16-bit' @@ -267,10 +192,10 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = bit16 AnchorSideBottom.Side = asrBottom - Left = 95 - Height = 19 + Left = 81 + Height = 15 Top = 0 - Width = 42 + Width = 34 Anchors = [akTop, akLeft, akBottom] BorderSpacing.Left = 8 BorderSpacing.Right = 4 @@ -291,7 +216,7 @@ object audiofrm: Taudiofrm Left = 0 Height = 1 Top = 0 - Width = 141 + Width = 119 Anchors = [akTop, akLeft, akRight] end object Bevel2: TBevel @@ -302,8 +227,8 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom Left = 0 Height = 1 - Top = 30 - Width = 141 + Top = 26 + Width = 119 Anchors = [akTop, akLeft, akRight] BorderSpacing.Top = 5 end @@ -311,28 +236,28 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = Bevel2 AnchorSideRight.Control = Bevel2 AnchorSideRight.Side = asrBottom - Left = 73 - Height = 61 - Top = 35 - Width = 64 + Left = 61 + Height = 49 + Top = 31 + Width = 54 Anchors = [akTop, akRight] AutoSize = True BorderSpacing.Left = 4 BorderSpacing.Top = 5 BorderSpacing.Right = 4 BevelOuter = bvNone - ClientHeight = 61 - ClientWidth = 64 + ClientHeight = 49 + ClientWidth = 54 ParentBackground = False TabOrder = 1 object khz96: TLabel AnchorSideTop.Control = RightHzPanel AnchorSideRight.Control = RightHzPanel AnchorSideRight.Side = asrBottom - Left = 19 - Height = 19 + Left = 17 + Height = 15 Top = 0 - Width = 45 + Width = 37 Anchors = [akTop, akRight] Caption = '96 khz' Color = clBlack @@ -348,9 +273,9 @@ object audiofrm: Taudiofrm AnchorSideRight.Control = khz96 AnchorSideRight.Side = asrBottom Left = 0 - Height = 19 - Top = 21 - Width = 64 + Height = 15 + Top = 17 + Width = 54 Anchors = [akTop, akRight] BorderSpacing.Top = 2 Caption = '176.4 khz' @@ -366,10 +291,10 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = khz96 AnchorSideRight.Side = asrBottom - Left = 16 - Height = 19 - Top = 42 - Width = 48 + Left = 13 + Height = 15 + Top = 34 + Width = 41 Anchors = [akTop, akRight] BorderSpacing.Top = 2 Caption = '192khz' @@ -385,25 +310,25 @@ object audiofrm: Taudiofrm AnchorSideLeft.Control = Bevel2 AnchorSideTop.Control = Bevel2 Left = 4 - Height = 61 - Top = 35 - Width = 57 + Height = 49 + Top = 31 + Width = 47 AutoSize = True BorderSpacing.Left = 4 BorderSpacing.Top = 5 BevelOuter = bvNone - ClientHeight = 61 - ClientWidth = 57 + ClientHeight = 49 + ClientWidth = 47 ParentBackground = False TabOrder = 2 object khz44: TLabel AnchorSideTop.Control = LeftHzPanel AnchorSideRight.Control = LeftHzPanel AnchorSideRight.Side = asrBottom - Left = 1 - Height = 19 + Left = 0 + Height = 15 Top = 0 - Width = 56 + Width = 47 Anchors = [akTop, akRight] Caption = '44.1 khz' Color = clBlack @@ -419,10 +344,10 @@ object audiofrm: Taudiofrm AnchorSideRight.Control = khz44 AnchorSideRight.Side = asrBottom AnchorSideBottom.Side = asrBottom - Left = 12 - Height = 19 - Top = 21 - Width = 45 + Left = 10 + Height = 15 + Top = 17 + Width = 37 Anchors = [akTop, akRight] BorderSpacing.Top = 2 Caption = '48 khz' @@ -441,9 +366,9 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom AnchorSideBottom.Side = asrBottom Left = 0 - Height = 19 - Top = 42 - Width = 57 + Height = 15 + Top = 34 + Width = 47 Anchors = [akTop, akRight] BorderSpacing.Top = 2 Caption = '88.2 khz' @@ -463,8 +388,8 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom Left = 0 Height = 1 - Top = 101 - Width = 141 + Top = 85 + Width = 119 Anchors = [akTop, akLeft, akRight] BorderSpacing.Top = 5 end @@ -473,10 +398,10 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = LeftHzPanel AnchorSideRight.Side = asrBottom - Left = 21 - Height = 19 - Top = 107 - Width = 40 + Left = 19 + Height = 15 + Top = 91 + Width = 32 Anchors = [akTop, akRight] BorderSpacing.Top = 5 Caption = 'mono' @@ -489,10 +414,10 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = RightHzPanel AnchorSideRight.Side = asrBottom - Left = 93 - Height = 19 - Top = 107 - Width = 44 + Left = 79 + Height = 15 + Top = 91 + Width = 36 Anchors = [akTop, akRight] BorderSpacing.Top = 5 Caption = 'stereo' @@ -508,26 +433,26 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideBottom.Control = DetailsPanel AnchorSideBottom.Side = asrBottom - Left = 177 - Height = 131 - Top = 29 - Width = 248 + Left = 155 + Height = 111 + Top = 25 + Width = 250 Anchors = [akTop, akLeft, akBottom] AutoSize = True BorderSpacing.Left = 10 BevelOuter = bvNone - ClientHeight = 131 - ClientWidth = 248 + ClientHeight = 111 + ClientWidth = 250 ParentBackground = False - TabOrder = 2 + TabOrder = 1 object drlb: TStaticText AnchorSideLeft.Control = DRPanel AnchorSideTop.Control = DRPanel AnchorSideBottom.Side = asrBottom Left = 0 - Height = 134 + Height = 112 Top = 0 - Width = 136 + Width = 138 AutoSize = True BorderSpacing.CellAlignVertical = ccaLeftTop Caption = 'DR' @@ -546,8 +471,8 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = DRPanel AnchorSideBottom.Control = drlb AnchorSideBottom.Side = asrBottom - Left = 141 - Height = 134 + Left = 143 + Height = 112 Top = 0 Width = 64 Anchors = [akTop, akLeft, akBottom] @@ -568,8 +493,8 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = drlb AnchorSideBottom.Control = drlb AnchorSideBottom.Side = asrBottom - Left = 148 - Height = 114 + Left = 150 + Height = 92 Top = 10 Width = 100 Anchors = [akTop, akLeft, akBottom] @@ -578,7 +503,7 @@ object audiofrm: Taudiofrm BorderSpacing.Bottom = 10 BevelColor = clNone BevelOuter = bvNone - ClientHeight = 114 + ClientHeight = 92 ClientWidth = 100 Color = clBlack ParentBackground = False @@ -586,7 +511,7 @@ object audiofrm: Taudiofrm TabOrder = 2 object progressbar: TBCRadialProgressBar Left = 0 - Height = 114 + Height = 92 Top = 0 Width = 100 Align = alClient @@ -602,6 +527,384 @@ object audiofrm: Taudiofrm end end end + object Notebook: TNotebook + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = BlocksBtn + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = Owner + AnchorSideBottom.Side = asrBottom + Left = 3 + Height = 368 + Top = 198 + Width = 694 + PageIndex = 1 + Anchors = [akTop, akLeft, akRight, akBottom] + BorderSpacing.Left = 3 + BorderSpacing.Top = 5 + BorderSpacing.Right = 3 + BorderSpacing.Bottom = 5 + TabOrder = 2 + object Page1: TPage + object dbchart: TChart + Left = 0 + Height = 368 + Top = 0 + Width = 694 + AllowPanning = False + AllowZoom = False + AxisList = < + item + Grid.Visible = False + Marks.LabelFont.Color = clWhite + Marks.LabelFont.Height = -13 + Marks.LabelFont.Name = 'Sans' + Marks.LabelBrush.Style = bsClear + Minors = <> + Range.Max = 100 + Range.UseMax = True + Range.UseMin = True + Title.LabelFont.Color = clWhite + Title.LabelFont.Height = -13 + Title.LabelFont.Name = 'Sans' + Title.LabelFont.Orientation = 900 + Title.LabelFont.Style = [fsBold] + Title.Visible = True + Title.Caption = 'dB' + Title.LabelBrush.Style = bsClear + end + item + Grid.Visible = False + Alignment = calBottom + Arrow.Visible = True + Marks.LabelFont.Color = clWhite + Marks.LabelBrush.Style = bsClear + Minors = <> + Range.Max = 25 + Range.UseMax = True + Range.UseMin = True + Title.LabelFont.Color = clWhite + Title.LabelFont.Height = -13 + Title.LabelFont.Name = 'Sans' + Title.LabelFont.Style = [fsBold] + Title.Visible = True + Title.Caption = 'Block-Num' + Title.LabelBrush.Style = bsClear + end> + BackColor = clBlack + Foot.Brush.Color = clBtnFace + Foot.Font.Color = clBlue + Title.Brush.Color = clBtnFace + Title.Font.Color = clBlue + Title.Text.Strings = ( + '' + ) + Align = alClient + Anchors = [] + Color = clBlack + object rmseries: TBarSeries + BarBrush.Color = clYellow + Source = rms + end + object peakseries: TBarSeries + BarBrush.Color = clRed + Source = peak + end + end + end + object Page2: TPage + object DurationPanel: TPanel + AnchorSideLeft.Control = Page2 + AnchorSideTop.Control = Page2 + AnchorSideBottom.Control = FrequencyPanel + Left = 10 + Height = 335 + Top = 6 + Width = 40 + Anchors = [akTop, akLeft, akBottom] + AutoSize = True + BorderSpacing.Left = 10 + BorderSpacing.Top = 6 + BorderSpacing.Bottom = 6 + BevelOuter = bvNone + Caption = 'Time' + ClientHeight = 335 + ClientWidth = 40 + Constraints.MinWidth = 40 + ParentBackground = False + TabOrder = 0 + object spectrumfirstvalue: TLabel + Left = 0 + Height = 15 + Top = 0 + Width = 40 + Align = alTop + Alignment = taRightJustify + Caption = '0:00' + end + object spectrumsecondvalue: TLabel + Left = 0 + Height = 15 + Top = 320 + Width = 40 + Align = alBottom + Alignment = taRightJustify + Caption = '0:00' + end + end + object FrequencyPanel: TPanel + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = Page2 + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = Page2 + AnchorSideBottom.Side = asrBottom + Left = 56 + Height = 15 + Top = 347 + Width = 612 + Anchors = [akLeft, akRight, akBottom] + AutoSize = True + BorderSpacing.Right = 26 + BorderSpacing.Bottom = 6 + BevelOuter = bvNone + Caption = 'Frequency' + ClientHeight = 15 + ClientWidth = 612 + TabOrder = 1 + object frequencyfirstvalue: TLabel + Left = 0 + Height = 15 + Top = 0 + Width = 25 + Align = alLeft + Caption = '0 Hz' + Layout = tlCenter + end + object frequencysecondvalue: TLabel + Left = 573 + Height = 15 + Top = 0 + Width = 39 + Align = alRight + Caption = '25 kHz' + Layout = tlCenter + end + end + object SpectrumImage: TImage + AnchorSideLeft.Control = DurationPanel + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = DurationPanel + AnchorSideRight.Control = FrequencyPanel + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = DurationPanel + AnchorSideBottom.Side = asrBottom + Left = 56 + Height = 335 + Top = 6 + Width = 612 + Anchors = [akTop, akLeft, akRight, akBottom] + BorderSpacing.Left = 6 + Stretch = True + end + end + end + object BlocksBtn: TBCButton + AnchorSideLeft.Control = audio + AnchorSideTop.Control = DetailsPanel + AnchorSideTop.Side = asrBottom + Left = 26 + Height = 32 + Top = 161 + Width = 120 + StateClicked.Background.Gradient1.StartColor = 8404992 + StateClicked.Background.Gradient1.EndColor = 4194304 + StateClicked.Background.Gradient1.GradientType = gtRadial + StateClicked.Background.Gradient1.Point1XPercent = 50 + StateClicked.Background.Gradient1.Point1YPercent = 100 + StateClicked.Background.Gradient1.Point2XPercent = 0 + StateClicked.Background.Gradient1.Point2YPercent = 0 + StateClicked.Background.Gradient2.StartColor = clWhite + StateClicked.Background.Gradient2.EndColor = clBlack + StateClicked.Background.Gradient2.GradientType = gtLinear + StateClicked.Background.Gradient2.Point1XPercent = 0 + StateClicked.Background.Gradient2.Point1YPercent = 0 + StateClicked.Background.Gradient2.Point2XPercent = 0 + StateClicked.Background.Gradient2.Point2YPercent = 100 + StateClicked.Background.Gradient1EndPercent = 100 + StateClicked.Background.Style = bbsColor + StateClicked.Border.Style = bboNone + StateClicked.FontEx.Color = 16770790 + StateClicked.FontEx.FontQuality = fqSystemClearType + StateClicked.FontEx.Shadow = True + StateClicked.FontEx.ShadowRadius = 2 + StateClicked.FontEx.ShadowOffsetX = 1 + StateClicked.FontEx.ShadowOffsetY = 1 + StateClicked.FontEx.Style = [fsBold] + StateHover.Background.Gradient1.StartColor = 16744448 + StateHover.Background.Gradient1.EndColor = 8404992 + StateHover.Background.Gradient1.GradientType = gtRadial + StateHover.Background.Gradient1.Point1XPercent = 50 + StateHover.Background.Gradient1.Point1YPercent = 100 + StateHover.Background.Gradient1.Point2XPercent = 0 + StateHover.Background.Gradient1.Point2YPercent = 0 + StateHover.Background.Gradient2.StartColor = clWhite + StateHover.Background.Gradient2.EndColor = clBlack + StateHover.Background.Gradient2.GradientType = gtLinear + StateHover.Background.Gradient2.Point1XPercent = 0 + StateHover.Background.Gradient2.Point1YPercent = 0 + StateHover.Background.Gradient2.Point2XPercent = 0 + StateHover.Background.Gradient2.Point2YPercent = 100 + StateHover.Background.Gradient1EndPercent = 100 + StateHover.Background.Style = bbsColor + StateHover.Border.Color = clWhite + StateHover.Border.Style = bboSolid + StateHover.FontEx.Color = clWhite + StateHover.FontEx.FontQuality = fqSystemClearType + StateHover.FontEx.Shadow = True + StateHover.FontEx.ShadowRadius = 2 + StateHover.FontEx.ShadowOffsetX = 1 + StateHover.FontEx.ShadowOffsetY = 1 + StateHover.FontEx.Style = [fsBold] + StateNormal.Background.Gradient1.StartColor = 4194304 + StateNormal.Background.Gradient1.EndColor = 8405056 + StateNormal.Background.Gradient1.GradientType = gtLinear + StateNormal.Background.Gradient1.Point1XPercent = 0 + StateNormal.Background.Gradient1.Point1YPercent = 0 + StateNormal.Background.Gradient1.Point2XPercent = 0 + StateNormal.Background.Gradient1.Point2YPercent = 100 + StateNormal.Background.Gradient2.StartColor = 8405056 + StateNormal.Background.Gradient2.EndColor = 4194304 + StateNormal.Background.Gradient2.GradientType = gtRadial + StateNormal.Background.Gradient2.Point1XPercent = 50 + StateNormal.Background.Gradient2.Point1YPercent = 100 + StateNormal.Background.Gradient2.Point2XPercent = 0 + StateNormal.Background.Gradient2.Point2YPercent = 0 + StateNormal.Background.Gradient1EndPercent = 60 + StateNormal.Background.Style = bbsColor + StateNormal.Border.Color = clWhite + StateNormal.Border.Style = bboSolid + StateNormal.FontEx.Color = 16770790 + StateNormal.FontEx.FontQuality = fqSystemClearType + StateNormal.FontEx.Shadow = False + StateNormal.FontEx.ShadowRadius = 2 + StateNormal.FontEx.ShadowOffsetX = 1 + StateNormal.FontEx.ShadowOffsetY = 1 + StateNormal.FontEx.Style = [fsBold] + BorderSpacing.Top = 25 + Caption = 'Blocks' + Color = clNone + DropDownWidth = 16 + DropDownArrowSize = 8 + GlobalOpacity = 255 + OnClick = BlocksBtnClick + ParentColor = False + Rounding.RoundX = 0 + Rounding.RoundY = 0 + RoundingDropDown.RoundX = 1 + RoundingDropDown.RoundY = 1 + TextApplyGlobalOpacity = False + MemoryUsage = bmuHigh + end + object SpectrumBtn: TBCButton + AnchorSideLeft.Control = BlocksBtn + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = DetailsPanel + AnchorSideTop.Side = asrBottom + Left = 156 + Height = 32 + Top = 161 + Width = 120 + StateClicked.Background.Gradient1.StartColor = 8404992 + StateClicked.Background.Gradient1.EndColor = 4194304 + StateClicked.Background.Gradient1.GradientType = gtRadial + StateClicked.Background.Gradient1.Point1XPercent = 50 + StateClicked.Background.Gradient1.Point1YPercent = 100 + StateClicked.Background.Gradient1.Point2XPercent = 0 + StateClicked.Background.Gradient1.Point2YPercent = 0 + StateClicked.Background.Gradient2.StartColor = clWhite + StateClicked.Background.Gradient2.EndColor = clBlack + StateClicked.Background.Gradient2.GradientType = gtLinear + StateClicked.Background.Gradient2.Point1XPercent = 0 + StateClicked.Background.Gradient2.Point1YPercent = 0 + StateClicked.Background.Gradient2.Point2XPercent = 0 + StateClicked.Background.Gradient2.Point2YPercent = 100 + StateClicked.Background.Gradient1EndPercent = 100 + StateClicked.Background.Style = bbsColor + StateClicked.Border.Style = bboNone + StateClicked.FontEx.Color = 16770790 + StateClicked.FontEx.FontQuality = fqSystemClearType + StateClicked.FontEx.Shadow = True + StateClicked.FontEx.ShadowRadius = 2 + StateClicked.FontEx.ShadowOffsetX = 1 + StateClicked.FontEx.ShadowOffsetY = 1 + StateClicked.FontEx.Style = [fsBold] + StateHover.Background.Gradient1.StartColor = 16744448 + StateHover.Background.Gradient1.EndColor = 8404992 + StateHover.Background.Gradient1.GradientType = gtRadial + StateHover.Background.Gradient1.Point1XPercent = 50 + StateHover.Background.Gradient1.Point1YPercent = 100 + StateHover.Background.Gradient1.Point2XPercent = 0 + StateHover.Background.Gradient1.Point2YPercent = 0 + StateHover.Background.Gradient2.StartColor = clWhite + StateHover.Background.Gradient2.EndColor = clBlack + StateHover.Background.Gradient2.GradientType = gtLinear + StateHover.Background.Gradient2.Point1XPercent = 0 + StateHover.Background.Gradient2.Point1YPercent = 0 + StateHover.Background.Gradient2.Point2XPercent = 0 + StateHover.Background.Gradient2.Point2YPercent = 100 + StateHover.Background.Gradient1EndPercent = 100 + StateHover.Background.Style = bbsColor + StateHover.Border.Color = clWhite + StateHover.Border.Style = bboSolid + StateHover.FontEx.Color = clWhite + StateHover.FontEx.FontQuality = fqSystemClearType + StateHover.FontEx.Shadow = True + StateHover.FontEx.ShadowRadius = 2 + StateHover.FontEx.ShadowOffsetX = 1 + StateHover.FontEx.ShadowOffsetY = 1 + StateHover.FontEx.Style = [fsBold] + StateNormal.Background.Gradient1.StartColor = 4194304 + StateNormal.Background.Gradient1.EndColor = 8405056 + StateNormal.Background.Gradient1.GradientType = gtLinear + StateNormal.Background.Gradient1.Point1XPercent = 0 + StateNormal.Background.Gradient1.Point1YPercent = 0 + StateNormal.Background.Gradient1.Point2XPercent = 0 + StateNormal.Background.Gradient1.Point2YPercent = 100 + StateNormal.Background.Gradient2.StartColor = 8405056 + StateNormal.Background.Gradient2.EndColor = 4194304 + StateNormal.Background.Gradient2.GradientType = gtRadial + StateNormal.Background.Gradient2.Point1XPercent = 50 + StateNormal.Background.Gradient2.Point1YPercent = 100 + StateNormal.Background.Gradient2.Point2XPercent = 0 + StateNormal.Background.Gradient2.Point2YPercent = 0 + StateNormal.Background.Gradient1EndPercent = 60 + StateNormal.Background.Style = bbsColor + StateNormal.Border.Color = clWhite + StateNormal.Border.Style = bboSolid + StateNormal.FontEx.Color = 16770790 + StateNormal.FontEx.FontQuality = fqSystemClearType + StateNormal.FontEx.Shadow = True + StateNormal.FontEx.ShadowRadius = 2 + StateNormal.FontEx.ShadowOffsetX = 1 + StateNormal.FontEx.ShadowOffsetY = 1 + StateNormal.FontEx.Style = [fsBold] + BorderSpacing.Left = 10 + BorderSpacing.Top = 25 + Caption = 'Spectrum' + Color = clNone + DropDownWidth = 16 + DropDownArrowSize = 8 + GlobalOpacity = 255 + OnClick = BlocksBtnClick + ParentColor = False + Rounding.RoundX = 0 + Rounding.RoundY = 0 + RoundingDropDown.RoundX = 1 + RoundingDropDown.RoundY = 1 + TextApplyGlobalOpacity = False + MemoryUsage = bmuHigh + end object filedialog: TOpenDialog Left = 504 Top = 16 diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 2e981bf..a0d3eb2 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -1,7 +1,7 @@ { Description: Main form. - Copyright (C) 2020-2023 Melchiorre Caruso + Copyright (C) 2020-2024 Melchiorre Caruso This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -27,13 +27,25 @@ interface uses classes, sysutils, forms, controls, graphics, dialogs, buttons, stdctrls, - extctrls, comctrls, tagraph, taseries, tasources, bufstream, soundwav, - bcradialprogressbar, bclistbox, process, inifiles; + extctrls, comctrls, tagraph, taseries, tasources, TAChartImageList, bufstream, + soundwav, bcradialprogressbar, bclistbox, BCComboBox, dtthemedgauge, + dtthemedclock, DTAnalogClock, BCLeaLCDDisplay, BGRASpeedButton, + ColorSpeedButton, BCButton, BCImageButton, BGRAVirtualScreen, process, + inifiles, BGRABitmap, BGRABitmapTypes, BCTypes; type { taudiofrm } taudiofrm = class(tform) + BlocksBtn: TBCButton; + FrequencyPanel: TPanel; + frequencyfirstvalue: TLabel; + frequencysecondvalue: TLabel; + SpectrumImage: TImage; + spectrumsecondvalue: TLabel; + spectrumfirstvalue: TLabel; + DurationPanel: TPanel; + SpectrumBtn: TBCButton; Bevel1: TBevel; Bevel2: TBevel; Bevel3: TBevel; @@ -43,6 +55,7 @@ taudiofrm = class(tform) btnfile: timage; btnfolder: timage; buttons: timagelist; + dbchart: TChart; drlb: TStaticText; drvalue: TStaticText; khz176: TLabel; @@ -56,19 +69,22 @@ taudiofrm = class(tform) LeftHzPanel: TPanel; mono: TLabel; DRPanel: TPanel; + Notebook: TNotebook; + Page1: TPage; + Page2: TPage; + peakseries: TBarSeries; progressbar: TBCRadialProgressBar; progresspanel: TPanel; RightHzPanel: TPanel; + rmseries: TBarSeries; stereo: TLabel; Report: TImageList; - peakseries: tbarseries; - rmseries: tbarseries; - dbchart: tchart; audio: tlabel; dirdialog: tselectdirectorydialog; peak: tlistchartsource; rms: tlistchartsource; filedialog: topendialog; + procedure blocksbtnclick(sender: tobject); procedure btnfolderclick(sender: tobject); procedure btnfoldermousedown(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); @@ -88,14 +104,15 @@ taudiofrm = class(tform) procedure btnfilemouseup(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); procedure formdestroy(sender: tobject); - procedure FormResize(Sender: TObject); + procedure formresize(sender: tobject); procedure onstart; procedure onstop; procedure onprogress; procedure clear; - - procedure btnloadicon(btn: timage; index: longint; x, y: longint); procedure execute; + procedure createspectrum(atrack: ttrack); + procedure btnloadicon(btn: timage; index: longint; x, y: longint); + private buffer: treadbufstream; stream: tfilestream; @@ -138,6 +155,15 @@ procedure taudiofrm.formcreate(sender: tobject); progressbar .value := 0; // inizialize mail form color := clblack; + // initialize buttons + blocksbtn .statenormal .fontex.shadow := false; + blocksbtn .statehover .fontex.shadow := false; + blocksbtn .stateclicked.fontex.shadow := false; + spectrumbtn.statenormal .fontex.shadow := false; + spectrumbtn.statehover .fontex.shadow := false; + spectrumbtn.stateclicked.fontex.shadow := false; + // initialize notebook + notebook.pageindex := 0; // inizialize clear; end; @@ -238,8 +264,8 @@ procedure taudiofrm.onstop; for j := 0 to track.channelcount -1 do peaki := peaki + track.channels[j].peak[i]; peak.add(i, max(0, db(peaki/track.channelcount*norm))); - end; end; + end; dbchart.bottomaxis.range.max := rms.count; dbchart.invalidate; @@ -283,6 +309,8 @@ procedure taudiofrm.onstop; btnfolder .enabled := true; progresspanel.visible := false; progressbar .value := 0; + // create spectrum image + createspectrum(track); end; wave := nil; @@ -341,13 +369,15 @@ procedure taudiofrm.clear; mono .font.color := clgray; stereo.font.color := clgray; - rms .clear; + rms.clear; peak.clear; audio.caption := 'Audio'; audio.font.color := clwhite; drvalue.caption := '--'; drvalue.font.color := clwhite; dbchart.invalidate; + + blocksbtnclick(nil); end; procedure taudiofrm.execute; @@ -370,6 +400,7 @@ procedure taudiofrm.execute; tempfile := includetrailingbackslash( gettempdir(false)) + 'audiometer-tmp.wav'; + // get file properties process := tprocess.create(nil); try process.parameters.clear; @@ -414,6 +445,7 @@ procedure taudiofrm.execute; end; process.destroy; + // decode to .wave process := tprocess.create(nil); try process.parameters.clear; @@ -442,6 +474,7 @@ procedure taudiofrm.execute; except end; process.destroy; + end else tempfile := track.name; @@ -466,6 +499,85 @@ procedure taudiofrm.execute; end; end; +function getcolor(factor: double): tbgrapixel; +var + r1, g1, b1, r2, g2, b2: byte; + color1: tbgrapixel; + color2: tbgrapixel; +begin + if factor < 1/3 then + begin + color1 := clyellow; + color2 := clred; + factor := (factor - 0) / (1/3); + end else + if factor < 2/3 then + begin + color1 := clred; + color2 := clpurple; + factor := (factor - 1/3) / (1/3); + end else + begin + color1 := clpurple; + color2 := clblue; + factor := (factor - 2/3) / (1/3); + end; + + r1 := color1.red; + g1 := color1.green; + b1 := color1.blue; + + r2 := color2.red; + g2 := color2.green; + b2 := color2.blue; + + result.red := trunc(r1 + (r2 - r1) * factor); + result.green := trunc(g1 + (g2 - g1) * factor); + result.blue := trunc(b1 + (b2 - b1) * factor); + result.alpha := 1; +end; + +procedure taudiofrm.createspectrum(atrack: ttrack); +var + bit: tbgrabitmap; + i, j, k: longint; + x, y, z: double; + px: tbgrapixel; +begin + bit := tbgrabitmap.create; + bit.setsize(spectrumimage.width, spectrumimage.height); + bit.filltransparent; + + for i := 0 to atrack.channelcount -1 do + begin + k := 0; + for j := 0 to atrack.channels[i].spectrum.count -1 do + begin + z := atrack.channels[i].spectrum[j] / atrack.samplerate * 2; + y := (bit.height-1) * j / atrack.channels[i].spectrum.count; + x := (bit.width -1) * z; + + px := bit.scanat(trunc(x), trunc(y)); + if px.alpha = 0 then + begin + px := getcolor(z); + end else + begin + if px.alpha < 255 then + px.alpha := px.alpha + 1; + end; + bit.setpixel(trunc(x), trunc(y), px); + end; + end; + bit.draw(spectrumimage.canvas, 0, 0, false); + bit.destroy; + + spectrumfirstvalue .caption := '0:00s'; + spectrumsecondvalue .caption := atrack.duration + 's'; + frequencyfirstvalue .caption := '0 Hz'; + frequencysecondvalue.caption := (atrack.samplerate div 2).tostring + ' Hz'; +end; + procedure taudiofrm.btnfileclick(sender: tobject); begin tracklist.clear; @@ -515,6 +627,51 @@ procedure taudiofrm.btnfolderclick(sender: tobject); execute; end; +procedure taudiofrm.blocksbtnclick(sender: tobject); +var + btn1: tbcbutton; + btn2: tbcbutton; +begin + if sender = nil then + begin + if notebook.pageindex = 0 then + begin + btn1 := blocksbtn; + btn2 := spectrumbtn; + end else + begin + btn1 := spectrumbtn; + btn2 := blocksbtn; + end; + end else + begin + btn1 := sender as tbcbutton; + if btn1 = blocksbtn then + btn2 := spectrumbtn + else + btn2 := blocksbtn; + + if btn1 = blocksbtn then notebook.pageindex := 0 else + if btn1 = spectrumbtn then notebook.pageindex := 1; + end; + + btn1.statenormal .background.color := clwhite; + btn1.statehover .background.color := clwhite; + btn1.stateclicked.background.color := clblack; + + btn1.statenormal .fontex .color := clblack; + btn1.statehover .fontex .color := clblack; + btn1.stateclicked.fontex .color := clwhite; + + btn2.statenormal .background.color := clblack; + btn2.statehover .background.color := clblack; + btn2.stateclicked.background.color := clwhite; + + btn2.statenormal .fontex .color := clwhite; + btn2.statehover .fontex .color := clwhite; + btn2.stateclicked.fontex .color := clblack; +end; + procedure taudiofrm.btnfilemouseup(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); begin diff --git a/src/soundwav.pas b/src/soundwav.pas index a82adc5..61dc057 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -1,7 +1,7 @@ { Description: Sound routines. - Copyright (C) 2020-2023 Melchiorre Caruso + Copyright (C) 2020-2024 Melchiorre Caruso This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -26,7 +26,7 @@ interface uses - classes, sysutils, math, fgl; + classes, sysutils, math, fgl, ufft, utypes; // WAVE utils @@ -95,6 +95,7 @@ ttrackchannel = class private frms2: tdoublelist; fpeak: tdoublelist; + fspectrum: tdoublelist; function getrms2(index: longint): double; function getpeak(index: longint): double; function getcount: longint; @@ -103,6 +104,7 @@ ttrackchannel = class destructor destroy; override; procedure add(const rms2i, peaki: double); public + property spectrum: tdoublelist read fspectrum; property rms2[index: longint]: double read getrms2; property peak[index: longint]: double read getpeak; property count: longint read getcount; @@ -165,6 +167,7 @@ ttrackanalyzer = class(tthread) procedure readheader(astream: tstream); function readsamples(astream: tstream; ablock: tsamples): longint; procedure readfromstream(astream: tstream); + procedure putfreq(block: tsamples; channel: word; var spectrum: tdoublelist); function getrms2(block: tsamples; channel: word): double; function getpeak(block: tsamples; channel: word): double; function getrms(channel: word): double; @@ -199,7 +202,7 @@ ttracklist = class procedure sort; procedure savetofile(const filename: string); public - property tracks[index: longint]: ttrack read gettrack; + property tracks[index: longint]: ttrack read gettrack; default; property count: longint read getcount; end; @@ -260,12 +263,14 @@ constructor ttrackchannel.create; inherited create; frms2 := tdoublelist.create; fpeak := tdoublelist.create; + fspectrum := tdoublelist.create; end; destructor ttrackchannel.destroy; begin frms2.destroy; fpeak.destroy; + fspectrum.destroy; inherited destroy; end; @@ -374,6 +379,32 @@ procedure ttrackanalyzer.clear; fstatus := 0; end; +procedure ttrackanalyzer.putfreq(block: tsamples; channel: word; var spectrum: tdoublelist); +var + ibuff: array of complex = nil; + i, j, k: longint; + freq: tcompvector; +begin + j := 0; + setlength(ibuff, 1024); + for i := low(block) to high(block) do + begin + ibuff[j].x := block[i].channelvalues[channel]; + ibuff[j].y := 0; + inc(j); + + if j = 1024 then + begin + freq := FFT(j, ibuff); + for k := low(freq) to high(freq) do + spectrum.add(sqrt(sqr(freq[k].x) + sqr(freq[k].y))); + freq := nil; + j := 0; + end; + end; + ibuff := nil; +end; + function ttrackanalyzer.getrms2(block: tsamples; channel: word): double; var i: longint; @@ -499,9 +530,10 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); block: tsamples; blocksize: longword; blockcount: longword; - i, j: longint; + i, j, k: longint; samples: longword; seconds: longword; + spectrum: tdoublelist; begin {$ifopt D+} writeln('track.name ', ftrack.fname); @@ -543,11 +575,14 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); if blockcount = 0 then fstatus := -3; if blocksize = 0 then fstatus := -3; if status <> 0 then exit; - // allobate new block (of 3 seconds length) + // allocate new block (of 3 seconds length) block := nil; setlength(block, blocksize); for i := low(block) to high(block) do setlength(block[i].channelvalues, ffmt.channels); + // clear track spectrum + for i := 0 to ftrack.channelcount -1 do + ftrack.fchannels[i].fspectrum.clear; // read samples for i := 0 to blockcount -1 do begin @@ -561,6 +596,9 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); begin fdata[j].add(getrms2(block, j), getpeak(block, j)); + + // calculate spectrum + putfreq(block, j, ftrack.fchannels[j].fspectrum); end; end; fpercentage := 100; From 3d74d912bce04ec0dd544b2070a0fac5c874874f Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sat, 23 Nov 2024 16:26:13 +0100 Subject: [PATCH 02/21] Updating spectrum routines --- audiometer.lpi | 4 +- src/mainfrm.lfm | 244 +++++++++++++++++++++++++---------------------- src/mainfrm.pas | 142 ++++++++++++++++----------- src/soundwav.pas | 120 ++++++++++++----------- 4 files changed, 281 insertions(+), 229 deletions(-) diff --git a/audiometer.lpi b/audiometer.lpi index 1711ea8..a5ad6ef 100644 --- a/audiometer.lpi +++ b/audiometer.lpi @@ -76,10 +76,10 @@ - + - + diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index aab8b18..2cddd9f 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -1,7 +1,7 @@ object audiofrm: Taudiofrm - Left = 356 + Left = 587 Height = 571 - Top = 111 + Top = 330 Width = 700 Caption = 'AudioMeter 0.4.6 - Dynamic Range Meter' ClientHeight = 571 @@ -11,20 +11,20 @@ object audiofrm: Taudiofrm Constraints.MinWidth = 700 Font.Color = clWhite Font.Style = [fsBold] + Position = poDesktopCenter + LCLVersion = '4.99.0.0' OnCloseQuery = FormCloseQuery OnCreate = FormCreate OnDestroy = FormDestroy OnResize = FormResize - Position = poDesktopCenter - LCLVersion = '3.6.0.0' object audio: TLabel AnchorSideLeft.Control = Owner AnchorSideTop.Control = Owner AnchorSideBottom.Side = asrBottom Left = 26 - Height = 15 + Height = 19 Top = 10 - Width = 32 + Width = 40 BorderSpacing.Left = 26 BorderSpacing.Top = 10 Caption = 'Audio' @@ -41,15 +41,10 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom Left = 611 Height = 32 - Top = 30 + Top = 34 Width = 32 Anchors = [akTop, akRight] BorderSpacing.Right = 45 - OnClick = btnfileClick - OnMouseDown = btnfileMouseDown - OnMouseLeave = btnfileMouseLeave - OnMouseMove = btnfileMouseMove - OnMouseUp = btnfileMouseUp Picture.Data = { 1754506F727461626C654E6574776F726B47726170686963C102000089504E47 0D0A1A0A0000000D49484452000000200000001E08060000004D0A1C29000000 @@ -79,6 +74,11 @@ object audiofrm: Taudiofrm StretchOutEnabled = False StretchInEnabled = False Transparent = True + OnClick = btnfileClick + OnMouseDown = btnfileMouseDown + OnMouseLeave = btnfileMouseLeave + OnMouseMove = btnfileMouseMove + OnMouseUp = btnfileMouseUp end object btnfolder: TImage AnchorSideTop.Control = audio @@ -87,16 +87,11 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom Left = 656 Height = 32 - Top = 30 + Top = 34 Width = 32 Anchors = [akTop, akRight] BorderSpacing.Top = 5 BorderSpacing.Right = 12 - OnClick = btnfolderclick - OnMouseDown = btnfolderMouseDown - OnMouseLeave = btnfoldermouseleave - OnMouseMove = btnfoldermousemove - OnMouseUp = btnfoldermouseup Picture.Data = { 1754506F727461626C654E6574776F726B47726170686963E701000089504E47 0D0A1A0A0000000D494844520000002000000015080600000027CDECEA000000 @@ -120,20 +115,25 @@ object audiofrm: Taudiofrm StretchOutEnabled = False StretchInEnabled = False Transparent = True + OnClick = btnfolderclick + OnMouseDown = btnfolderMouseDown + OnMouseLeave = btnfoldermouseleave + OnMouseMove = btnfoldermousemove + OnMouseUp = btnfoldermouseup end object DetailsPanel: TPanel AnchorSideLeft.Control = audio AnchorSideTop.Control = audio AnchorSideTop.Side = asrBottom Left = 26 - Height = 106 - Top = 30 - Width = 119 + Height = 126 + Top = 34 + Width = 141 AutoSize = True BorderSpacing.Top = 5 BevelOuter = bvNone - ClientHeight = 106 - ClientWidth = 119 + ClientHeight = 126 + ClientWidth = 141 Color = clBlack ParentBackground = False ParentColor = False @@ -143,22 +143,22 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = Bevel1 AnchorSideTop.Side = asrBottom Left = 0 - Height = 15 + Height = 19 Top = 6 - Width = 119 + Width = 141 AutoSize = True BorderSpacing.Top = 5 BevelOuter = bvNone - ClientHeight = 15 - ClientWidth = 119 + ClientHeight = 19 + ClientWidth = 141 ParentBackground = False TabOrder = 0 object bit8: TLabel AnchorSideLeft.Control = BitsPanel Left = 4 - Height = 15 + Height = 19 Top = 0 - Width = 27 + Width = 34 BorderSpacing.Left = 4 Caption = '8-bit' Color = clBlack @@ -174,10 +174,10 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = bit8 AnchorSideBottom.Control = bit8 AnchorSideBottom.Side = asrBottom - Left = 39 - Height = 15 + Left = 46 + Height = 19 Top = 0 - Width = 34 + Width = 41 Anchors = [akTop, akLeft, akBottom] BorderSpacing.Left = 8 Caption = '16-bit' @@ -192,10 +192,10 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom AnchorSideBottom.Control = bit16 AnchorSideBottom.Side = asrBottom - Left = 81 - Height = 15 + Left = 95 + Height = 19 Top = 0 - Width = 34 + Width = 42 Anchors = [akTop, akLeft, akBottom] BorderSpacing.Left = 8 BorderSpacing.Right = 4 @@ -216,7 +216,7 @@ object audiofrm: Taudiofrm Left = 0 Height = 1 Top = 0 - Width = 119 + Width = 141 Anchors = [akTop, akLeft, akRight] end object Bevel2: TBevel @@ -227,8 +227,8 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom Left = 0 Height = 1 - Top = 26 - Width = 119 + Top = 30 + Width = 141 Anchors = [akTop, akLeft, akRight] BorderSpacing.Top = 5 end @@ -236,28 +236,28 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = Bevel2 AnchorSideRight.Control = Bevel2 AnchorSideRight.Side = asrBottom - Left = 61 - Height = 49 - Top = 31 - Width = 54 + Left = 73 + Height = 61 + Top = 35 + Width = 64 Anchors = [akTop, akRight] AutoSize = True BorderSpacing.Left = 4 BorderSpacing.Top = 5 BorderSpacing.Right = 4 BevelOuter = bvNone - ClientHeight = 49 - ClientWidth = 54 + ClientHeight = 61 + ClientWidth = 64 ParentBackground = False TabOrder = 1 object khz96: TLabel AnchorSideTop.Control = RightHzPanel AnchorSideRight.Control = RightHzPanel AnchorSideRight.Side = asrBottom - Left = 17 - Height = 15 + Left = 19 + Height = 19 Top = 0 - Width = 37 + Width = 45 Anchors = [akTop, akRight] Caption = '96 khz' Color = clBlack @@ -273,9 +273,9 @@ object audiofrm: Taudiofrm AnchorSideRight.Control = khz96 AnchorSideRight.Side = asrBottom Left = 0 - Height = 15 - Top = 17 - Width = 54 + Height = 19 + Top = 21 + Width = 64 Anchors = [akTop, akRight] BorderSpacing.Top = 2 Caption = '176.4 khz' @@ -291,10 +291,10 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = khz96 AnchorSideRight.Side = asrBottom - Left = 13 - Height = 15 - Top = 34 - Width = 41 + Left = 16 + Height = 19 + Top = 42 + Width = 48 Anchors = [akTop, akRight] BorderSpacing.Top = 2 Caption = '192khz' @@ -310,25 +310,25 @@ object audiofrm: Taudiofrm AnchorSideLeft.Control = Bevel2 AnchorSideTop.Control = Bevel2 Left = 4 - Height = 49 - Top = 31 - Width = 47 + Height = 61 + Top = 35 + Width = 57 AutoSize = True BorderSpacing.Left = 4 BorderSpacing.Top = 5 BevelOuter = bvNone - ClientHeight = 49 - ClientWidth = 47 + ClientHeight = 61 + ClientWidth = 57 ParentBackground = False TabOrder = 2 object khz44: TLabel AnchorSideTop.Control = LeftHzPanel AnchorSideRight.Control = LeftHzPanel AnchorSideRight.Side = asrBottom - Left = 0 - Height = 15 + Left = 1 + Height = 19 Top = 0 - Width = 47 + Width = 56 Anchors = [akTop, akRight] Caption = '44.1 khz' Color = clBlack @@ -344,10 +344,10 @@ object audiofrm: Taudiofrm AnchorSideRight.Control = khz44 AnchorSideRight.Side = asrBottom AnchorSideBottom.Side = asrBottom - Left = 10 - Height = 15 - Top = 17 - Width = 37 + Left = 12 + Height = 19 + Top = 21 + Width = 45 Anchors = [akTop, akRight] BorderSpacing.Top = 2 Caption = '48 khz' @@ -366,9 +366,9 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom AnchorSideBottom.Side = asrBottom Left = 0 - Height = 15 - Top = 34 - Width = 47 + Height = 19 + Top = 42 + Width = 57 Anchors = [akTop, akRight] BorderSpacing.Top = 2 Caption = '88.2 khz' @@ -388,8 +388,8 @@ object audiofrm: Taudiofrm AnchorSideRight.Side = asrBottom Left = 0 Height = 1 - Top = 85 - Width = 119 + Top = 101 + Width = 141 Anchors = [akTop, akLeft, akRight] BorderSpacing.Top = 5 end @@ -398,10 +398,10 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = LeftHzPanel AnchorSideRight.Side = asrBottom - Left = 19 - Height = 15 - Top = 91 - Width = 32 + Left = 21 + Height = 19 + Top = 107 + Width = 40 Anchors = [akTop, akRight] BorderSpacing.Top = 5 Caption = 'mono' @@ -414,10 +414,10 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = RightHzPanel AnchorSideRight.Side = asrBottom - Left = 79 - Height = 15 - Top = 91 - Width = 36 + Left = 93 + Height = 19 + Top = 107 + Width = 44 Anchors = [akTop, akRight] BorderSpacing.Top = 5 Caption = 'stereo' @@ -433,16 +433,16 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideBottom.Control = DetailsPanel AnchorSideBottom.Side = asrBottom - Left = 155 - Height = 111 - Top = 25 - Width = 250 + Left = 177 + Height = 131 + Top = 29 + Width = 248 Anchors = [akTop, akLeft, akBottom] AutoSize = True BorderSpacing.Left = 10 BevelOuter = bvNone - ClientHeight = 111 - ClientWidth = 250 + ClientHeight = 131 + ClientWidth = 248 ParentBackground = False TabOrder = 1 object drlb: TStaticText @@ -450,9 +450,9 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = DRPanel AnchorSideBottom.Side = asrBottom Left = 0 - Height = 112 + Height = 134 Top = 0 - Width = 138 + Width = 136 AutoSize = True BorderSpacing.CellAlignVertical = ccaLeftTop Caption = 'DR' @@ -471,8 +471,8 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = DRPanel AnchorSideBottom.Control = drlb AnchorSideBottom.Side = asrBottom - Left = 143 - Height = 112 + Left = 141 + Height = 134 Top = 0 Width = 64 Anchors = [akTop, akLeft, akBottom] @@ -493,8 +493,8 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = drlb AnchorSideBottom.Control = drlb AnchorSideBottom.Side = asrBottom - Left = 150 - Height = 92 + Left = 148 + Height = 114 Top = 10 Width = 100 Anchors = [akTop, akLeft, akBottom] @@ -503,7 +503,7 @@ object audiofrm: Taudiofrm BorderSpacing.Bottom = 10 BevelColor = clNone BevelOuter = bvNone - ClientHeight = 92 + ClientHeight = 114 ClientWidth = 100 Color = clBlack ParentBackground = False @@ -511,7 +511,7 @@ object audiofrm: Taudiofrm TabOrder = 2 object progressbar: TBCRadialProgressBar Left = 0 - Height = 92 + Height = 114 Top = 0 Width = 100 Align = alClient @@ -536,8 +536,8 @@ object audiofrm: Taudiofrm AnchorSideBottom.Control = Owner AnchorSideBottom.Side = asrBottom Left = 3 - Height = 368 - Top = 198 + Height = 344 + Top = 222 Width = 694 PageIndex = 1 Anchors = [akTop, akLeft, akRight, akBottom] @@ -549,7 +549,7 @@ object audiofrm: Taudiofrm object Page1: TPage object dbchart: TChart Left = 0 - Height = 368 + Height = 344 Top = 0 Width = 694 AllowPanning = False @@ -619,7 +619,7 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = Page2 AnchorSideBottom.Control = FrequencyPanel Left = 10 - Height = 335 + Height = 307 Top = 6 Width = 40 Anchors = [akTop, akLeft, akBottom] @@ -629,14 +629,14 @@ object audiofrm: Taudiofrm BorderSpacing.Bottom = 6 BevelOuter = bvNone Caption = 'Time' - ClientHeight = 335 + ClientHeight = 307 ClientWidth = 40 Constraints.MinWidth = 40 ParentBackground = False TabOrder = 0 object spectrumfirstvalue: TLabel Left = 0 - Height = 15 + Height = 19 Top = 0 Width = 40 Align = alTop @@ -645,8 +645,8 @@ object audiofrm: Taudiofrm end object spectrumsecondvalue: TLabel Left = 0 - Height = 15 - Top = 320 + Height = 19 + Top = 288 Width = 40 Align = alBottom Alignment = taRightJustify @@ -660,8 +660,8 @@ object audiofrm: Taudiofrm AnchorSideBottom.Control = Page2 AnchorSideBottom.Side = asrBottom Left = 56 - Height = 15 - Top = 347 + Height = 19 + Top = 319 Width = 612 Anchors = [akLeft, akRight, akBottom] AutoSize = True @@ -669,29 +669,30 @@ object audiofrm: Taudiofrm BorderSpacing.Bottom = 6 BevelOuter = bvNone Caption = 'Frequency' - ClientHeight = 15 + ClientHeight = 19 ClientWidth = 612 + ParentBackground = False TabOrder = 1 object frequencyfirstvalue: TLabel Left = 0 - Height = 15 + Height = 19 Top = 0 - Width = 25 + Width = 30 Align = alLeft Caption = '0 Hz' Layout = tlCenter end object frequencysecondvalue: TLabel - Left = 573 - Height = 15 + Left = 567 + Height = 19 Top = 0 - Width = 39 + Width = 45 Align = alRight Caption = '25 kHz' Layout = tlCenter end end - object SpectrumImage: TImage + object SpectrumPanel: TPanel AnchorSideLeft.Control = DurationPanel AnchorSideLeft.Side = asrBottom AnchorSideTop.Control = DurationPanel @@ -700,12 +701,27 @@ object audiofrm: Taudiofrm AnchorSideBottom.Control = DurationPanel AnchorSideBottom.Side = asrBottom Left = 56 - Height = 335 + Height = 307 Top = 6 Width = 612 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 6 - Stretch = True + BevelOuter = bvNone + ClientHeight = 307 + ClientWidth = 612 + ParentBackground = False + TabOrder = 2 + object SpectrumImage: TBGRAVirtualScreen + Left = 0 + Height = 307 + Top = 0 + Width = 612 + OnRedraw = SpectrumImageRedraw + BitmapAutoScale = False + Align = alClient + Alignment = taLeftJustify + TabOrder = 0 + end end end end @@ -715,7 +731,7 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom Left = 26 Height = 32 - Top = 161 + Top = 185 Width = 120 StateClicked.Background.Gradient1.StartColor = 8404992 StateClicked.Background.Gradient1.EndColor = 4194304 @@ -813,7 +829,7 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom Left = 156 Height = 32 - Top = 161 + Top = 185 Width = 120 StateClicked.Background.Gradient1.StartColor = 8404992 StateClicked.Background.Gradient1.EndColor = 4194304 diff --git a/src/mainfrm.pas b/src/mainfrm.pas index a0d3eb2..4d9fc4c 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -27,11 +27,9 @@ interface uses classes, sysutils, forms, controls, graphics, dialogs, buttons, stdctrls, - extctrls, comctrls, tagraph, taseries, tasources, TAChartImageList, bufstream, - soundwav, bcradialprogressbar, bclistbox, BCComboBox, dtthemedgauge, - dtthemedclock, DTAnalogClock, BCLeaLCDDisplay, BGRASpeedButton, - ColorSpeedButton, BCButton, BCImageButton, BGRAVirtualScreen, process, - inifiles, BGRABitmap, BGRABitmapTypes, BCTypes; + extctrls, comctrls, tagraph, taseries, tasources, bufstream, soundwav, + bcradialprogressbar, bclistbox, bcbutton, bgravirtualscreen, process, + inifiles, bgrabitmap, bgrabitmaptypes, bctypes; type { taudiofrm } @@ -41,7 +39,8 @@ taudiofrm = class(tform) FrequencyPanel: TPanel; frequencyfirstvalue: TLabel; frequencysecondvalue: TLabel; - SpectrumImage: TImage; + SpectrumImage: TBGRAVirtualScreen; + SpectrumPanel: TPanel; spectrumsecondvalue: TLabel; spectrumfirstvalue: TLabel; DurationPanel: TPanel; @@ -86,23 +85,17 @@ taudiofrm = class(tform) filedialog: topendialog; procedure blocksbtnclick(sender: tobject); procedure btnfolderclick(sender: tobject); - procedure btnfoldermousedown(sender: tobject; button: tmousebutton; - shift: tshiftstate; x, y: integer); + procedure btnfoldermousedown(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); procedure btnfoldermouseleave(sender: tobject); - procedure btnfoldermousemove(sender: tobject; - shift: tshiftstate; x, y: integer); - procedure btnfoldermouseup(sender: tobject; button: tmousebutton; - shift: tshiftstate; x, y: integer); + procedure btnfoldermousemove(sender: tobject; shift: tshiftstate; x, y: integer); + procedure btnfoldermouseup(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); procedure formclosequery(sender: tobject; var canclose: boolean); procedure formcreate(sender: tobject); procedure btnfileclick(sender: tobject); - procedure btnfilemousedown(sender: tobject; button: tmousebutton; - shift: tshiftstate; x, y: integer); + procedure btnfilemousedown(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); procedure btnfilemouseleave(sender: tobject); - procedure btnfilemousemove(sender: tobject; - shift: tshiftstate; x, y: integer); - procedure btnfilemouseup(sender: tobject; button: tmousebutton; - shift: tshiftstate; x, y: integer); + procedure btnfilemousemove(sender: tobject; shift: tshiftstate; x, y: integer); + procedure btnfilemouseup(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); procedure formdestroy(sender: tobject); procedure formresize(sender: tobject); procedure onstart; @@ -110,9 +103,9 @@ taudiofrm = class(tform) procedure onprogress; procedure clear; procedure execute; - procedure createspectrum(atrack: ttrack); + function drawspectrum(atrack: ttrack): tbgrabitmap; procedure btnloadicon(btn: timage; index: longint; x, y: longint); - + procedure SpectrumImageRedraw(Sender: TObject; Bitmap: TBGRABitmap); private buffer: treadbufstream; stream: tfilestream; @@ -310,7 +303,7 @@ procedure taudiofrm.onstop; progresspanel.visible := false; progressbar .value := 0; // create spectrum image - createspectrum(track); + spectrumimage.redrawbitmap; end; wave := nil; @@ -505,22 +498,34 @@ function getcolor(factor: double): tbgrapixel; color1: tbgrapixel; color2: tbgrapixel; begin - if factor < 1/3 then + if factor < 1/5 then begin - color1 := clyellow; - color2 := clred; - factor := (factor - 0) / (1/3); + color1 := clblack; + color2 := clblue; + factor := (factor - 0) / (1/5); end else - if factor < 2/3 then + if factor < 2/5 then begin - color1 := clred; + color1 := clblue; color2 := clpurple; - factor := (factor - 1/3) / (1/3); + factor := (factor - 1/5) / (1/5); end else + if factor < 3/5 then begin color1 := clpurple; - color2 := clblue; - factor := (factor - 2/3) / (1/3); + color2 := clred; + factor := (factor - 2/5) / (1/5); + end else + if factor < 4/5 then + begin + color1 := clred; + color2 := clyellow; + factor := (factor - 3/5) / (1/5); + end else + begin + color1 := clyellow; + color2 := clwhite; + factor := (factor - 4/5) / (1/5); end; r1 := color1.red; @@ -534,43 +539,50 @@ function getcolor(factor: double): tbgrapixel; result.red := trunc(r1 + (r2 - r1) * factor); result.green := trunc(g1 + (g2 - g1) * factor); result.blue := trunc(b1 + (b2 - b1) * factor); - result.alpha := 1; + result.alpha := 255; end; -procedure taudiofrm.createspectrum(atrack: ttrack); +function taudiofrm.drawspectrum(atrack: ttrack): tbgrabitmap; var - bit: tbgrabitmap; - i, j, k: longint; + i, j, k, q: longint; x, y, z: double; - px: tbgrapixel; + index: longint; + windowsize: longint; + windowcount: longint; + zmax: double; + zmin: double; begin - bit := tbgrabitmap.create; - bit.setsize(spectrumimage.width, spectrumimage.height); - bit.filltransparent; + result := tbgrabitmap.create; + result.setsize(spectrumpanel.width, spectrumpanel.height); + result.filltransparent; + zmax := minfloat; + zmin := maxfloat; for i := 0 to atrack.channelcount -1 do - begin - k := 0; - for j := 0 to atrack.channels[i].spectrum.count -1 do + for j := 0 to length(atrack.channels[i].spectrum) -1 do begin - z := atrack.channels[i].spectrum[j] / atrack.samplerate * 2; - y := (bit.height-1) * j / atrack.channels[i].spectrum.count; - x := (bit.width -1) * z; + if zmin > atrack.channels[i].spectrum[j] then + zmin := atrack.channels[i].spectrum[j]; - px := bit.scanat(trunc(x), trunc(y)); - if px.alpha = 0 then - begin - px := getcolor(z); - end else + if zmax < atrack.channels[i].spectrum[j] then + zmax := atrack.channels[i].spectrum[j]; + end; + + for i := 0 to result.width -1 do + for j := 0 to result.height -1 do + begin + z := 0; + for k := 0 to atrack.channelcount -1 do begin - if px.alpha < 255 then - px.alpha := px.alpha + 1; + windowsize := 512; + windowcount := length(atrack.channels[k].spectrum) div windowsize; + + index := trunc((j+1)/result.height*(windowcount -1))*windowsize + trunc(i/result.width*windowsize); + + z := z + db(atrack.channels[k].spectrum[index] - zmin)/db(zmax - zmin); end; - bit.setpixel(trunc(x), trunc(y), px); + result.setpixel(i, j, getcolor(z/atrack.channelcount)); end; - end; - bit.draw(spectrumimage.canvas, 0, 0, false); - bit.destroy; spectrumfirstvalue .caption := '0:00s'; spectrumsecondvalue .caption := atrack.duration + 's'; @@ -729,5 +741,25 @@ procedure taudiofrm.btnloadicon(btn: timage; index: longint; x, y: longint); buttons.draw(btn.canvas, x, y, index); end; +procedure taudiofrm.spectrumimageredraw(sender: tobject; bitmap: tbgrabitmap); +var + bit: tbgrabitmap; +begin + if tracklist.count > 0 then + begin + if trackindex < tracklist.count then + bit := drawspectrum(tracklist[trackindex]) + else + bit := drawspectrum(tracklist[tracklist.count -1]); + end else + begin + bit := tbgrabitmap.create; + bit.setsize(spectrumimage.width, spectrumimage.height); + bit.filltransparent; + end; + bitmap.putimage(0, 0, bit, dmset, 255); + bit.destroy; +end; + end. diff --git a/src/soundwav.pas b/src/soundwav.pas index 61dc057..ca79a42 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -81,21 +81,20 @@ interface end; // and after this header the actual data comes, which is an array of samples - tsample = packed record - channelvalues : array of longint; - end; + tdoublelist = specialize tfpglist; - tsamples = array of tsample; + tsamples = array of longint; - tdoublelist = specialize tfpglist; + tspectrum = array of double; // ttrackchannel ttrackchannel = class private + fsamples: tsamples; frms2: tdoublelist; fpeak: tdoublelist; - fspectrum: tdoublelist; + fspectrum: tspectrum; function getrms2(index: longint): double; function getpeak(index: longint): double; function getcount: longint; @@ -104,12 +103,16 @@ ttrackchannel = class destructor destroy; override; procedure add(const rms2i, peaki: double); public - property spectrum: tdoublelist read fspectrum; + property spectrum: tspectrum read fspectrum; property rms2[index: longint]: double read getrms2; property peak[index: longint]: double read getpeak; property count: longint read getcount; end; + // ttrackchannels + + ttrackchannels = array of ttrackchannel; + // ttrack ttrack = class @@ -119,7 +122,7 @@ ttrack = class fnumber: longint; fsamplerate: longword; fbitspersample: longint; - fchannels: array of ttrackchannel; + fchannels: ttrackchannels; fbyterate: longint; frms: double; fpeak: double; @@ -165,11 +168,11 @@ ttrackanalyzer = class(tthread) fnorm: longint; function getpercentage: longint; procedure readheader(astream: tstream); - function readsamples(astream: tstream; ablock: tsamples): longint; + function readsamples(astream: tstream; achannels: ttrackchannels; achannelsize: longint): longint; procedure readfromstream(astream: tstream); - procedure putfreq(block: tsamples; channel: word; var spectrum: tdoublelist); - function getrms2(block: tsamples; channel: word): double; - function getpeak(block: tsamples; channel: word): double; + procedure putfreq(achannel: ttrackchannel; var spectrum: tspectrum); + function getrms2(block: tsamples; index, count: longint): double; + function getpeak(block: tsamples; index, count: longint): double; function getrms(channel: word): double; function getpeak(channel: word): double; function getdr(channel: word): double; @@ -263,14 +266,14 @@ constructor ttrackchannel.create; inherited create; frms2 := tdoublelist.create; fpeak := tdoublelist.create; - fspectrum := tdoublelist.create; + fsamples := nil; end; destructor ttrackchannel.destroy; begin frms2.destroy; fpeak.destroy; - fspectrum.destroy; + fsamples := nil; inherited destroy; end; @@ -379,25 +382,31 @@ procedure ttrackanalyzer.clear; fstatus := 0; end; -procedure ttrackanalyzer.putfreq(block: tsamples; channel: word; var spectrum: tdoublelist); +procedure ttrackanalyzer.putfreq(achannel: ttrackchannel; var spectrum: tspectrum); +const + windowsize = 1024; var - ibuff: array of complex = nil; - i, j, k: longint; + i, j, k, index: longint; + ibuff: tcompvector = nil; freq: tcompvector; begin j := 0; - setlength(ibuff, 1024); - for i := low(block) to high(block) do + index := 0; + setlength(ibuff, windowsize); + for i := 0 to length(achannel.fsamples) -1 do begin - ibuff[j].x := block[i].channelvalues[channel]; + ibuff[j].x := achannel.fsamples[i]; ibuff[j].y := 0; inc(j); - if j = 1024 then + if j = windowsize then begin freq := FFT(j, ibuff); - for k := low(freq) to high(freq) do - spectrum.add(sqrt(sqr(freq[k].x) + sqr(freq[k].y))); + for k := 0 to length(freq) div 2 -1 do + begin + spectrum[index] := sqrt(sqr(freq[k].x / windowsize) + sqr(freq[k].y / windowsize)); + inc(index); + end; freq := nil; j := 0; end; @@ -405,26 +414,26 @@ procedure ttrackanalyzer.putfreq(block: tsamples; channel: word; var spectrum: t ibuff := nil; end; -function ttrackanalyzer.getrms2(block: tsamples; channel: word): double; +function ttrackanalyzer.getrms2(block: tsamples; index, count: longint): double; var i: longint; begin result := 0; - for i := low(block) to high(block) do + for i := index to (index + count) -1 do begin - result := result + sqr(block[i].channelvalues[channel]/fnorm); + result := result + sqr(block[i]/fnorm); end; - result := 2*result/length(block); + result := 2*result/count; end; -function ttrackanalyzer.getpeak(block: tsamples; channel: word): double; +function ttrackanalyzer.getpeak(block: tsamples; index, count: longint): double; var i : longint; begin result := 0; - for i := low(block) to high(block) do + for i := index to (index + count) -1 do begin - result := max(result, abs(block[i].channelvalues[channel])/fnorm); + result := max(result, abs(block[i])/fnorm); end; end; @@ -527,13 +536,11 @@ procedure ttrackanalyzer.execute; procedure ttrackanalyzer.readfromstream(astream:tstream); var - block: tsamples; blocksize: longword; blockcount: longword; i, j, k: longint; samples: longword; seconds: longword; - spectrum: tdoublelist; begin {$ifopt D+} writeln('track.name ', ftrack.fname); @@ -575,39 +582,35 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); if blockcount = 0 then fstatus := -3; if blocksize = 0 then fstatus := -3; if status <> 0 then exit; - // allocate new block (of 3 seconds length) - block := nil; - setlength(block, blocksize); - for i := low(block) to high(block) do - setlength(block[i].channelvalues, ffmt.channels); - // clear track spectrum - for i := 0 to ftrack.channelcount -1 do - ftrack.fchannels[i].fspectrum.clear; + // allocate samples array + setlength(ftrack.fchannels, ffmt.channels); + for i := 0 to length(ftrack.fchannels) -1 do + setlength(ftrack.fchannels[i].fsamples, samples); + // allocate spectrum array + for i := 0 to length(ftrack.fchannels) -1 do + setlength(ftrack.fchannels[i].fspectrum, samples div 2); // read samples + readsamples(astream, ftrack.fchannels, samples); for i := 0 to blockcount -1 do begin - fpercentage := 100*i/blockcount; + fpercentage := 100*(i + 1)/blockcount; if assigned(fonprogress) then Queue(fonprogress); - readsamples(astream, block); // calculate block rms and peak for j := 0 to ffmt.channels -1 do begin - fdata[j].add(getrms2(block, j), - getpeak(block, j)); - - // calculate spectrum - putfreq(block, j, ftrack.fchannels[j].fspectrum); + fdata[j].add(getrms2(ftrack.channels[j].fsamples, i * blocksize, blocksize), + getpeak(ftrack.channels[j].fsamples, i * blocksize, blocksize)); end; end; + // calculate spectrum + for i := 0 to ffmt.channels -1 do + putfreq(ftrack.fchannels[i], ftrack.fchannels[i].fspectrum); + fpercentage := 100; if assigned(fonprogress) then Queue(fonprogress); - // de-allocate block - for i := low(block) to high(block) do - block[i].channelvalues := nil; - block := nil; end; procedure ttrackanalyzer.readheader(astream: tstream); @@ -689,24 +692,25 @@ procedure ttrackanalyzer.readheader(astream: tstream); if fdatachunk.subck2size = 0 then fstatus := -2; end; -function ttrackanalyzer.readsamples(astream: tstream; ablock: tsamples): longint; +function ttrackanalyzer.readsamples(astream: tstream; achannels: ttrackchannels; achannelsize: longint): longint; var i, j, k: longint; dt : array[0..3] of byte; + zmin, zmax: longint; begin - result := length(ablock); - for i := low(ablock) to high(ablock) do + result := 0; + for i := 0 to achannelsize -1 do for j := 0 to ffmt.channels -1 do begin if ffmt.bitspersample = 8 then begin if astream.read(dt[0], 1) <> 1 then fstatus := -1; - ablock[i].channelvalues[j] := pbyte(@dt[0])^; + achannels[j].fsamples[i] := pbyte(@dt[0])^; end else if ffmt.bitspersample = 16 then begin if astream.read(dt[0], 2) <> 2 then fstatus := -1; - ablock[i].channelvalues[j] := psmallint(@dt[0])^; + achannels[j].fsamples[i] := psmallint(@dt[0])^; end else if ffmt.bitspersample = 24 then begin @@ -721,12 +725,12 @@ function ttrackanalyzer.readsamples(astream: tstream; ablock: tsamples): longint k := (k shl 8) or dt[1]; k := (k shl 8) or dt[0]; - ablock[i].channelvalues[j] := k; + achannels[j].fsamples[i] := k; end else if ffmt.bitspersample = 32 then begin if astream.read(dt[0], 4) <> 4 then fstatus := -1; - ablock[i].channelvalues[j] := plongint(@dt[0])^; + achannels[j].fsamples[i] := plongint(@dt[0])^; end; end; end; From 654c870b3a12ee44457182f398c35ccf34073e9a Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sat, 23 Nov 2024 16:36:46 +0100 Subject: [PATCH 03/21] Updating version info --- src/mainfrm.pas | 2 +- src/soundwav.pas | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 4d9fc4c..2bb24f0 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -338,7 +338,7 @@ procedure taudiofrm.onstop; report.clear; *) end; - {$ifopt D-} sleep(800); {$endif} + {$ifopt D-} sleep(500); {$endif} execute; end; diff --git a/src/soundwav.pas b/src/soundwav.pas index ca79a42..c1f86fd 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -803,7 +803,7 @@ procedure ttracklist.savetofile(const filename: string); track: ttrack; begin s := tstringlist.create; - s.add('AudioMeter 0.4.4 - Dynamic Range Meter'); + s.add('AudioMeter 0.4.6 - Dynamic Range Meter'); s.add(splitter); s.add(format('Log date : %s', [datetimetostr(now)])); s.add(splitter); From 6f6f42dca9c5b42b427dd80a63107a5117d2e200 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sat, 23 Nov 2024 16:51:59 +0100 Subject: [PATCH 04/21] Updated spectrum updating mode --- src/mainfrm.lfm | 4 +++- src/mainfrm.pas | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index 2cddd9f..84836b3 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -539,7 +539,7 @@ object audiofrm: Taudiofrm Height = 344 Top = 222 Width = 694 - PageIndex = 1 + PageIndex = 0 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 3 BorderSpacing.Top = 5 @@ -720,6 +720,8 @@ object audiofrm: Taudiofrm BitmapAutoScale = False Align = alClient Alignment = taLeftJustify + Color = clWhite + ParentColor = False TabOrder = 0 end end diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 2bb24f0..7f45512 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -303,7 +303,8 @@ procedure taudiofrm.onstop; progresspanel.visible := false; progressbar .value := 0; // create spectrum image - spectrumimage.redrawbitmap; + if notebook.pageindex = 1 then + spectrumimage.redrawbitmap; end; wave := nil; @@ -682,6 +683,10 @@ procedure taudiofrm.blocksbtnclick(sender: tobject); btn2.statenormal .fontex .color := clwhite; btn2.statehover .fontex .color := clwhite; btn2.stateclicked.fontex .color := clblack; + + // update spectrum image + if notebook.PageIndex = 1 then + spectrumimage.redrawbitmap; end; procedure taudiofrm.btnfilemouseup(sender: tobject; button: tmousebutton; From 24d271ce74a74476f8bf9b8a3f3be15182bbd8e1 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sat, 23 Nov 2024 17:29:54 +0100 Subject: [PATCH 05/21] Fix instability bug --- src/mainfrm.pas | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 7f45512..e18ef26 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -223,7 +223,6 @@ procedure taudiofrm.onstop; //begin //deletefile(tempfile); //end; - if wave.status <> 0 then begin trackindex := tracklist.count; @@ -302,7 +301,7 @@ procedure taudiofrm.onstop; btnfolder .enabled := true; progresspanel.visible := false; progressbar .value := 0; - // create spectrum image + // update spectrum image if notebook.pageindex = 1 then spectrumimage.redrawbitmap; end; @@ -559,31 +558,35 @@ function taudiofrm.drawspectrum(atrack: ttrack): tbgrabitmap; zmax := minfloat; zmin := maxfloat; - for i := 0 to atrack.channelcount -1 do - for j := 0 to length(atrack.channels[i].spectrum) -1 do - begin - if zmin > atrack.channels[i].spectrum[j] then - zmin := atrack.channels[i].spectrum[j]; - if zmax < atrack.channels[i].spectrum[j] then - zmax := atrack.channels[i].spectrum[j]; - end; + if atrack.channelcount > 0 then + begin + for i := 0 to atrack.channelcount -1 do + for j := 0 to length(atrack.channels[i].spectrum) -1 do + begin + if zmin > atrack.channels[i].spectrum[j] then + zmin := atrack.channels[i].spectrum[j]; - for i := 0 to result.width -1 do - for j := 0 to result.height -1 do - begin - z := 0; - for k := 0 to atrack.channelcount -1 do + if zmax < atrack.channels[i].spectrum[j] then + zmax := atrack.channels[i].spectrum[j]; + end; + + for i := 0 to result.width -1 do + for j := 0 to result.height -1 do begin - windowsize := 512; - windowcount := length(atrack.channels[k].spectrum) div windowsize; + z := 0; + for k := 0 to atrack.channelcount -1 do + begin + windowsize := 512; + windowcount := length(atrack.channels[k].spectrum) div windowsize; - index := trunc((j+1)/result.height*(windowcount -1))*windowsize + trunc(i/result.width*windowsize); + index := trunc((j+1)/result.height*(windowcount -1))*windowsize + trunc(i/result.width*windowsize); - z := z + db(atrack.channels[k].spectrum[index] - zmin)/db(zmax - zmin); + z := z + db(atrack.channels[k].spectrum[index] - zmin)/db(zmax - zmin); + end; + result.setpixel(i, j, getcolor(z/atrack.channelcount)); end; - result.setpixel(i, j, getcolor(z/atrack.channelcount)); - end; + end; spectrumfirstvalue .caption := '0:00s'; spectrumsecondvalue .caption := atrack.duration + 's'; @@ -685,7 +688,7 @@ procedure taudiofrm.blocksbtnclick(sender: tobject); btn2.stateclicked.fontex .color := clblack; // update spectrum image - if notebook.PageIndex = 1 then + if notebook.pageindex = 1 then spectrumimage.redrawbitmap; end; From 174006a26d668e97118262ae3faab6e24d9f17bd Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 24 Nov 2024 10:53:32 +0100 Subject: [PATCH 06/21] Fix spectrum time label caption --- src/mainfrm.lfm | 6 +++--- src/mainfrm.pas | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index 84836b3..ae3f016 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -539,7 +539,7 @@ object audiofrm: Taudiofrm Height = 344 Top = 222 Width = 694 - PageIndex = 0 + PageIndex = 1 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 3 BorderSpacing.Top = 5 @@ -641,7 +641,7 @@ object audiofrm: Taudiofrm Width = 40 Align = alTop Alignment = taRightJustify - Caption = '0:00' + Caption = '0:00s' end object spectrumsecondvalue: TLabel Left = 0 @@ -650,7 +650,7 @@ object audiofrm: Taudiofrm Width = 40 Align = alBottom Alignment = taRightJustify - Caption = '0:00' + Caption = '0:00s' end end object FrequencyPanel: TPanel diff --git a/src/mainfrm.pas b/src/mainfrm.pas index e18ef26..010b27b 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -589,7 +589,11 @@ function taudiofrm.drawspectrum(atrack: ttrack): tbgrabitmap; end; spectrumfirstvalue .caption := '0:00s'; - spectrumsecondvalue .caption := atrack.duration + 's'; + if atrack.duration = '' then + spectrumsecondvalue .caption := '0:00s' + else + spectrumsecondvalue .caption := atrack.duration + 's'; + frequencyfirstvalue .caption := '0 Hz'; frequencysecondvalue.caption := (atrack.samplerate div 2).tostring + ' Hz'; end; From 91654c98f00b87550139b049544570a323367ec7 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 24 Nov 2024 13:13:36 +0100 Subject: [PATCH 07/21] Deallocating samples buffer --- src/mainfrm.lfm | 2 +- src/soundwav.pas | 93 +++++++++++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index ae3f016..131dc3a 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -539,7 +539,7 @@ object audiofrm: Taudiofrm Height = 344 Top = 222 Width = 694 - PageIndex = 1 + PageIndex = 0 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 3 BorderSpacing.Top = 5 diff --git a/src/soundwav.pas b/src/soundwav.pas index c1f86fd..5af2947 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -81,20 +81,19 @@ interface end; // and after this header the actual data comes, which is an array of samples - tdoublelist = specialize tfpglist; - - tsamples = array of longint; - - tspectrum = array of double; + tfloatlist = specialize tfpglist; + tspectrum = array of double; + tchannel = array of longint; + tchannels = array of tchannel; // ttrackchannel ttrackchannel = class private - fsamples: tsamples; - frms2: tdoublelist; - fpeak: tdoublelist; + frms2: tfloatlist; + fpeak: tfloatlist; fspectrum: tspectrum; + fspectrumws: longint; function getrms2(index: longint): double; function getpeak(index: longint): double; function getcount: longint; @@ -104,6 +103,7 @@ ttrackchannel = class procedure add(const rms2i, peaki: double); public property spectrum: tspectrum read fspectrum; + property spectrumws: longint read fspectrumws; property rms2[index: longint]: double read getrms2; property peak[index: longint]: double read getpeak; property count: longint read getcount; @@ -168,11 +168,11 @@ ttrackanalyzer = class(tthread) fnorm: longint; function getpercentage: longint; procedure readheader(astream: tstream); - function readsamples(astream: tstream; achannels: ttrackchannels; achannelsize: longint): longint; + function readsamples(astream: tstream; achannels: tchannels; achannelsize: longint): longint; procedure readfromstream(astream: tstream); - procedure putfreq(achannel: ttrackchannel; var spectrum: tspectrum); - function getrms2(block: tsamples; index, count: longint): double; - function getpeak(block: tsamples; index, count: longint): double; + procedure putfreq(achannel: tchannel; var spectrum: tspectrum; spectrumws: longint); + function getrms2(achannel: tchannel; index, count: longint): double; + function getpeak(achannel: tchannel; index, count: longint): double; function getrms(channel: word): double; function getpeak(channel: word): double; function getdr(channel: word): double; @@ -264,16 +264,15 @@ function db(const value: double): double; constructor ttrackchannel.create; begin inherited create; - frms2 := tdoublelist.create; - fpeak := tdoublelist.create; - fsamples := nil; + frms2 := tfloatlist.create; + fpeak := tfloatlist.create; + fspectrumws := 1024; end; destructor ttrackchannel.destroy; begin frms2.destroy; fpeak.destroy; - fsamples := nil; inherited destroy; end; @@ -382,9 +381,7 @@ procedure ttrackanalyzer.clear; fstatus := 0; end; -procedure ttrackanalyzer.putfreq(achannel: ttrackchannel; var spectrum: tspectrum); -const - windowsize = 1024; +procedure ttrackanalyzer.putfreq(achannel: tchannel; var spectrum: tspectrum; spectrumws: longint); var i, j, k, index: longint; ibuff: tcompvector = nil; @@ -392,19 +389,19 @@ procedure ttrackanalyzer.putfreq(achannel: ttrackchannel; var spectrum: tspectru begin j := 0; index := 0; - setlength(ibuff, windowsize); - for i := 0 to length(achannel.fsamples) -1 do + setlength(ibuff, spectrumws); + for i := 0 to length(achannel) -1 do begin - ibuff[j].x := achannel.fsamples[i]; + ibuff[j].x := achannel[i]; ibuff[j].y := 0; inc(j); - if j = windowsize then + if j = spectrumws then begin freq := FFT(j, ibuff); for k := 0 to length(freq) div 2 -1 do begin - spectrum[index] := sqrt(sqr(freq[k].x / windowsize) + sqr(freq[k].y / windowsize)); + spectrum[index] := sqrt(sqr(freq[k].x / spectrumws) + sqr(freq[k].y / spectrumws)); inc(index); end; freq := nil; @@ -414,26 +411,26 @@ procedure ttrackanalyzer.putfreq(achannel: ttrackchannel; var spectrum: tspectru ibuff := nil; end; -function ttrackanalyzer.getrms2(block: tsamples; index, count: longint): double; +function ttrackanalyzer.getrms2(achannel: tchannel; index, count: longint): double; var i: longint; begin result := 0; for i := index to (index + count) -1 do begin - result := result + sqr(block[i]/fnorm); + result := result + sqr(achannel[i]/fnorm); end; result := 2*result/count; end; -function ttrackanalyzer.getpeak(block: tsamples; index, count: longint): double; +function ttrackanalyzer.getpeak(achannel: tchannel; index, count: longint): double; var i : longint; begin result := 0; for i := index to (index + count) -1 do begin - result := max(result, abs(block[i])/fnorm); + result := max(result, abs(achannel[i])/fnorm); end; end; @@ -536,9 +533,10 @@ procedure ttrackanalyzer.execute; procedure ttrackanalyzer.readfromstream(astream:tstream); var + i, j: longint; blocksize: longword; blockcount: longword; - i, j, k: longint; + channels: tchannels = nil; samples: longword; seconds: longword; begin @@ -567,7 +565,7 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); seconds := (fdatachunk.subck2size div ffmt.blockalign) div ftrack.fsamplerate; ftrack.fduration := format('%3.2d:%2.2d', [seconds div (60), seconds mod (60)]); end; - // read samples + // read samplecount if status <> 0 then exit; setlength(fdata, ffmt.channels); for i := low(fdata) to high(fdata) do @@ -582,31 +580,36 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); if blockcount = 0 then fstatus := -3; if blocksize = 0 then fstatus := -3; if status <> 0 then exit; - // allocate samples array + // allocate track channels setlength(ftrack.fchannels, ffmt.channels); - for i := 0 to length(ftrack.fchannels) -1 do - setlength(ftrack.fchannels[i].fsamples, samples); - // allocate spectrum array + // allocate track channels spectrum for i := 0 to length(ftrack.fchannels) -1 do setlength(ftrack.fchannels[i].fspectrum, samples div 2); + // allocate channels buffer + setlength(channels, ffmt.channels); + for i := 0 to length(ftrack.fchannels) -1 do + setlength(channels[i], samples); // read samples - readsamples(astream, ftrack.fchannels, samples); + readsamples(astream, channels, samples); for i := 0 to blockcount -1 do begin fpercentage := 100*(i + 1)/blockcount; if assigned(fonprogress) then - Queue(fonprogress); - + queue(fonprogress); // calculate block rms and peak for j := 0 to ffmt.channels -1 do begin - fdata[j].add(getrms2(ftrack.channels[j].fsamples, i * blocksize, blocksize), - getpeak(ftrack.channels[j].fsamples, i * blocksize, blocksize)); + fdata[j].add(getrms2(channels[j], i * blocksize, blocksize), + getpeak(channels[j], i * blocksize, blocksize)); end; end; // calculate spectrum for i := 0 to ffmt.channels -1 do - putfreq(ftrack.fchannels[i], ftrack.fchannels[i].fspectrum); + putfreq(channels[i], ftrack.fchannels[i].fspectrum, ftrack.fchannels[i].spectrumws); + // de-allocate channels buffer + for i := 0 to length(ftrack.fchannels) -1 do + setlength(channels[i], 0); + setlength(channels, 0); fpercentage := 100; if assigned(fonprogress) then @@ -692,7 +695,7 @@ procedure ttrackanalyzer.readheader(astream: tstream); if fdatachunk.subck2size = 0 then fstatus := -2; end; -function ttrackanalyzer.readsamples(astream: tstream; achannels: ttrackchannels; achannelsize: longint): longint; +function ttrackanalyzer.readsamples(astream: tstream; achannels: tchannels; achannelsize: longint): longint; var i, j, k: longint; dt : array[0..3] of byte; @@ -705,12 +708,12 @@ function ttrackanalyzer.readsamples(astream: tstream; achannels: ttrackchannels; if ffmt.bitspersample = 8 then begin if astream.read(dt[0], 1) <> 1 then fstatus := -1; - achannels[j].fsamples[i] := pbyte(@dt[0])^; + achannels[j][i] := pbyte(@dt[0])^; end else if ffmt.bitspersample = 16 then begin if astream.read(dt[0], 2) <> 2 then fstatus := -1; - achannels[j].fsamples[i] := psmallint(@dt[0])^; + achannels[j][i] := psmallint(@dt[0])^; end else if ffmt.bitspersample = 24 then begin @@ -725,12 +728,12 @@ function ttrackanalyzer.readsamples(astream: tstream; achannels: ttrackchannels; k := (k shl 8) or dt[1]; k := (k shl 8) or dt[0]; - achannels[j].fsamples[i] := k; + achannels[j][i] := k; end else if ffmt.bitspersample = 32 then begin if astream.read(dt[0], 4) <> 4 then fstatus := -1; - achannels[j].fsamples[i] := plongint(@dt[0])^; + achannels[j][i] := plongint(@dt[0])^; end; end; end; From 3b660c7146481979c39a32ed8cc6777d8eb88a2a Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 24 Nov 2024 14:32:30 +0100 Subject: [PATCH 08/21] Updating CPU targhet --- audiometer.lpi | 1 + 1 file changed, 1 insertion(+) diff --git a/audiometer.lpi b/audiometer.lpi index a5ad6ef..46bce02 100644 --- a/audiometer.lpi +++ b/audiometer.lpi @@ -132,6 +132,7 @@ + From e6e9bfb3d31a2ba34f4931e92010fe848ff68af4 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 24 Nov 2024 15:00:27 +0100 Subject: [PATCH 09/21] Updating CPU target --- audiometer.lpi | 1 + 1 file changed, 1 insertion(+) diff --git a/audiometer.lpi b/audiometer.lpi index 46bce02..096f0e6 100644 --- a/audiometer.lpi +++ b/audiometer.lpi @@ -46,6 +46,7 @@ + From 4f1bf545b22b395baba67bf355b33c7025f9c5c4 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 24 Nov 2024 17:56:39 +0100 Subject: [PATCH 10/21] Reducing memory consuption --- src/mainfrm.pas | 36 ++++++++++++++++++++---------------- src/soundwav.pas | 9 +++++++++ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 010b27b..87d68d5 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -320,24 +320,28 @@ procedure taudiofrm.onstop; //mycapture.free; inc(trackindex); - if trackindex = tracklist.count then - begin - // save text report - tracklist.savetofile(trackfile); - // save png report - (* - mycapture := tbitmap.create; - mycapture.setsize(446, report.count*146); - mycapture.canvas.fillrect(0, 0, 446, report.count*146); - for i := 0 to report.count -1 do + // release unused spectrum + if trackindex < tracklist.count then + tracklist.tracks[trackindex -1].clearspectrum + else + if trackindex = tracklist.count then begin - report.draw(mycapture.canvas, 0, i*146, i); + // save text report + tracklist.savetofile(trackfile); + // save png report + (* + mycapture := tbitmap.create; + mycapture.setsize(446, report.count*146); + mycapture.canvas.fillrect(0, 0, 446, report.count*146); + for i := 0 to report.count -1 do + begin + report.draw(mycapture.canvas, 0, i*146, i); + end; + mycapture.savetofile(changefileext(trackfile, '.png')); + mycapture.destroy; + report.clear; + *) end; - mycapture.savetofile(changefileext(trackfile, '.png')); - mycapture.destroy; - report.clear; - *) - end; {$ifopt D-} sleep(500); {$endif} execute; end; diff --git a/src/soundwav.pas b/src/soundwav.pas index 5af2947..b8dca4d 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -134,6 +134,7 @@ ttrack = class public constructor create(const filename: string); destructor destroy; override; + procedure clearspectrum; public property name: string read fname; property album: string read falbum; @@ -326,6 +327,14 @@ destructor ttrack.destroy; inherited destroy; end; +procedure ttrack.clearspectrum; +var + i: longint; +begin + for i := 0 to length(fchannels) -1 do + setlength(fchannels[i].fspectrum, 0); +end; + function ttrack.getchannel(index: longint): ttrackchannel; begin result := fchannels[index]; From 7dbf27a88d789a79931d3ca35e8c8fe86cef82db Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Mon, 25 Nov 2024 22:33:18 +0100 Subject: [PATCH 11/21] Adding Hanning window --- src/soundwav.pas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/soundwav.pas b/src/soundwav.pas index b8dca4d..c3dada3 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -401,7 +401,7 @@ procedure ttrackanalyzer.putfreq(achannel: tchannel; var spectrum: tspectrum; sp setlength(ibuff, spectrumws); for i := 0 to length(achannel) -1 do begin - ibuff[j].x := achannel[i]; + ibuff[j].x := (0.5-0.5*cos(2*pi*j/(spectrumws-1)))*achannel[i]; ibuff[j].y := 0; inc(j); @@ -410,7 +410,7 @@ procedure ttrackanalyzer.putfreq(achannel: tchannel; var spectrum: tspectrum; sp freq := FFT(j, ibuff); for k := 0 to length(freq) div 2 -1 do begin - spectrum[index] := sqrt(sqr(freq[k].x / spectrumws) + sqr(freq[k].y / spectrumws)); + spectrum[index] := 2*sqrt(sqr(freq[k].x) + sqr(freq[k].y))/spectrumws; inc(index); end; freq := nil; From bcd2378e62ac00597acc1155a5539bf29e580cb5 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Mon, 25 Nov 2024 22:33:33 +0100 Subject: [PATCH 12/21] Update mainfrm.pas --- src/mainfrm.pas | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 87d68d5..e075738 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -502,15 +502,18 @@ function getcolor(factor: double): tbgrapixel; color1: tbgrapixel; color2: tbgrapixel; begin + if factor < 0 then exit(clblack); + if factor > 1 then exit(clwhite); + if factor < 1/5 then begin color1 := clblack; - color2 := clblue; + color2 := clnavy; factor := (factor - 0) / (1/5); end else if factor < 2/5 then begin - color1 := clblue; + color1 := clnavy; color2 := clpurple; factor := (factor - 1/5) / (1/5); end else @@ -548,45 +551,30 @@ function getcolor(factor: double): tbgrapixel; function taudiofrm.drawspectrum(atrack: ttrack): tbgrabitmap; var - i, j, k, q: longint; - x, y, z: double; + i, j, k: longint; + z: double; index: longint; windowsize: longint; windowcount: longint; - zmax: double; - zmin: double; begin result := tbgrabitmap.create; result.setsize(spectrumpanel.width, spectrumpanel.height); result.filltransparent; - zmax := minfloat; - zmin := maxfloat; - if atrack.channelcount > 0 then begin - for i := 0 to atrack.channelcount -1 do - for j := 0 to length(atrack.channels[i].spectrum) -1 do - begin - if zmin > atrack.channels[i].spectrum[j] then - zmin := atrack.channels[i].spectrum[j]; - - if zmax < atrack.channels[i].spectrum[j] then - zmax := atrack.channels[i].spectrum[j]; - end; - for i := 0 to result.width -1 do for j := 0 to result.height -1 do begin z := 0; for k := 0 to atrack.channelcount -1 do begin - windowsize := 512; + windowsize := atrack.channels[k].spectrumws div 2; windowcount := length(atrack.channels[k].spectrum) div windowsize; index := trunc((j+1)/result.height*(windowcount -1))*windowsize + trunc(i/result.width*windowsize); - z := z + db(atrack.channels[k].spectrum[index] - zmin)/db(zmax - zmin); + z := z + log10(2*atrack.channels[k].spectrum[index])/log10(intpower(2, atrack.bitspersample)); end; result.setpixel(i, j, getcolor(z/atrack.channelcount)); end; From 5cebcd2e07b3504946accc0afbd19eb0f36979dd Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Mon, 25 Nov 2024 22:37:07 +0100 Subject: [PATCH 13/21] Removing delay --- src/mainfrm.pas | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mainfrm.pas b/src/mainfrm.pas index e075738..43c5046 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -342,7 +342,6 @@ procedure taudiofrm.onstop; report.clear; *) end; - {$ifopt D-} sleep(500); {$endif} execute; end; From a8f75651297e652c1a10518f3e0a30dca78276ed Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Tue, 26 Nov 2024 19:44:59 +0100 Subject: [PATCH 14/21] Bugfix --- src/mainfrm.lfm | 4 +-- src/mainfrm.pas | 64 ++++++++++++++++++++++++------------------------ src/soundwav.pas | 25 ++++++++----------- 3 files changed, 43 insertions(+), 50 deletions(-) diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index 131dc3a..789c197 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -539,7 +539,7 @@ object audiofrm: Taudiofrm Height = 344 Top = 222 Width = 694 - PageIndex = 0 + PageIndex = 1 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 3 BorderSpacing.Top = 5 @@ -720,8 +720,6 @@ object audiofrm: Taudiofrm BitmapAutoScale = False Align = alClient Alignment = taLeftJustify - Color = clWhite - ParentColor = False TabOrder = 0 end end diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 43c5046..420acaf 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -115,6 +115,7 @@ taudiofrm = class(tform) trackfile: string; tempfile: string; wave: ttrackanalyzer; + working: boolean; public end; @@ -157,7 +158,10 @@ procedure taudiofrm.formcreate(sender: tobject); spectrumbtn.stateclicked.fontex.shadow := false; // initialize notebook notebook.pageindex := 0; + // initialize spectrumimage + spectrumimage.parentcolor := true; // inizialize + working := false; clear; end; @@ -192,7 +196,7 @@ procedure taudiofrm.formclosequery(sender: tobject; var canclose: boolean); procedure taudiofrm.onstart; begin clear; - + working := true; audio.font.color := clwhite; audio.caption := extractfilename(tracklist.tracks[trackindex].name); while (audio.Left + audio.Width) > (btnFolder.Left + btnFolder.Width) do @@ -301,9 +305,6 @@ procedure taudiofrm.onstop; btnfolder .enabled := true; progresspanel.visible := false; progressbar .value := 0; - // update spectrum image - if notebook.pageindex = 1 then - spectrumimage.redrawbitmap; end; wave := nil; @@ -320,28 +321,28 @@ procedure taudiofrm.onstop; //mycapture.free; inc(trackindex); - // release unused spectrum - if trackindex < tracklist.count then - tracklist.tracks[trackindex -1].clearspectrum - else - if trackindex = tracklist.count then + if trackindex = tracklist.count then + begin + // save text report + tracklist.savetofile(trackfile); + // save png report + (* + mycapture := tbitmap.create; + mycapture.setsize(446, report.count*146); + mycapture.canvas.fillrect(0, 0, 446, report.count*146); + for i := 0 to report.count -1 do begin - // save text report - tracklist.savetofile(trackfile); - // save png report - (* - mycapture := tbitmap.create; - mycapture.setsize(446, report.count*146); - mycapture.canvas.fillrect(0, 0, 446, report.count*146); - for i := 0 to report.count -1 do - begin - report.draw(mycapture.canvas, 0, i*146, i); - end; - mycapture.savetofile(changefileext(trackfile, '.png')); - mycapture.destroy; - report.clear; - *) + report.draw(mycapture.canvas, 0, i*146, i); end; + mycapture.savetofile(changefileext(trackfile, '.png')); + mycapture.destroy; + report.clear; + *) + working := false; + // update spectrum image + if notebook.pageindex = 1 then + spectrumimage.redrawbitmap; + end; execute; end; @@ -482,7 +483,7 @@ procedure taudiofrm.execute; if assigned(stream) then begin buffer := treadbufstream.create(stream); - wave := ttrackanalyzer.create(track, buffer); + wave := ttrackanalyzer.create(track, buffer, trackindex = tracklist.count -1); wave.onstart := @onstart; wave.onstop := @onstop; wave.onprogress := @onprogress; @@ -748,20 +749,19 @@ procedure taudiofrm.spectrumimageredraw(sender: tobject; bitmap: tbgrabitmap); var bit: tbgrabitmap; begin - if tracklist.count > 0 then + if (working = false) and (tracklist.count > 0) then begin - if trackindex < tracklist.count then - bit := drawspectrum(tracklist[trackindex]) - else - bit := drawspectrum(tracklist[tracklist.count -1]); + bit := tbgrabitmap.create; + bit := drawspectrum(tracklist[tracklist.count -1]); + bitmap.putimage(0, 0, bit, dmset, 255); + bit.destroy; end else begin bit := tbgrabitmap.create; bit.setsize(spectrumimage.width, spectrumimage.height); bit.filltransparent; + bit.destroy; end; - bitmap.putimage(0, 0, bit, dmset, 255); - bit.destroy; end; end. diff --git a/src/soundwav.pas b/src/soundwav.pas index c3dada3..79fbb8e 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -134,7 +134,6 @@ ttrack = class public constructor create(const filename: string); destructor destroy; override; - procedure clearspectrum; public property name: string read fname; property album: string read falbum; @@ -167,6 +166,7 @@ ttrackanalyzer = class(tthread) fonstop: tthreadmethod; fonprogress: tthreadmethod; fnorm: longint; + fspectrumon: boolean; function getpercentage: longint; procedure readheader(astream: tstream); function readsamples(astream: tstream; achannels: tchannels; achannelsize: longint): longint; @@ -179,7 +179,7 @@ ttrackanalyzer = class(tthread) function getdr(channel: word): double; procedure clear; public - constructor create(atrack: ttrack; astream: tstream); + constructor create(atrack: ttrack; astream: tstream; aspectrumon: boolean); destructor destroy; override; procedure execute; override; public @@ -327,14 +327,6 @@ destructor ttrack.destroy; inherited destroy; end; -procedure ttrack.clearspectrum; -var - i: longint; -begin - for i := 0 to length(fchannels) -1 do - setlength(fchannels[i].fspectrum, 0); -end; - function ttrack.getchannel(index: longint): ttrackchannel; begin result := fchannels[index]; @@ -361,13 +353,14 @@ function ttrack.getchannelcount: longint; // ttrackanalyzer -constructor ttrackanalyzer.create(atrack: ttrack; astream: tstream); +constructor ttrackanalyzer.create(atrack: ttrack; astream: tstream; aspectrumon: boolean); begin fdata := nil; ftrack := atrack; fstream := astream; clear; + fspectrumon := aspectrumon; freeonterminate := true; inherited create(true); end; @@ -592,8 +585,9 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); // allocate track channels setlength(ftrack.fchannels, ffmt.channels); // allocate track channels spectrum - for i := 0 to length(ftrack.fchannels) -1 do - setlength(ftrack.fchannels[i].fspectrum, samples div 2); + if fspectrumon then + for i := 0 to length(ftrack.fchannels) -1 do + setlength(ftrack.fchannels[i].fspectrum, samples div 2); // allocate channels buffer setlength(channels, ffmt.channels); for i := 0 to length(ftrack.fchannels) -1 do @@ -613,8 +607,9 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); end; end; // calculate spectrum - for i := 0 to ffmt.channels -1 do - putfreq(channels[i], ftrack.fchannels[i].fspectrum, ftrack.fchannels[i].spectrumws); + if fspectrumon then + for i := 0 to ffmt.channels -1 do + putfreq(channels[i], ftrack.fchannels[i].fspectrum, ftrack.fchannels[i].spectrumws); // de-allocate channels buffer for i := 0 to length(ftrack.fchannels) -1 do setlength(channels[i], 0); From 2a0c47cd2b5225dc1fd99e7f8067d31920519ede Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Tue, 26 Nov 2024 20:29:02 +0100 Subject: [PATCH 15/21] Update mainfrm.pas --- src/mainfrm.pas | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 420acaf..3202035 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -574,7 +574,7 @@ function taudiofrm.drawspectrum(atrack: ttrack): tbgrabitmap; index := trunc((j+1)/result.height*(windowcount -1))*windowsize + trunc(i/result.width*windowsize); - z := z + log10(2*atrack.channels[k].spectrum[index])/log10(intpower(2, atrack.bitspersample)); + z := z + db(2*atrack.channels[k].spectrum[index])/db(1 shl atrack.bitspersample); end; result.setpixel(i, j, getcolor(z/atrack.channelcount)); end; From 31efcbb68eaf6603f380b05013f38d5d6b4aa009 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Wed, 27 Nov 2024 22:45:37 +0100 Subject: [PATCH 16/21] Fixing progress bar --- src/soundwav.pas | 116 ++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/src/soundwav.pas b/src/soundwav.pas index 79fbb8e..2cbdff5 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -81,10 +81,10 @@ interface end; // and after this header the actual data comes, which is an array of samples - tfloatlist = specialize tfpglist; - tspectrum = array of double; - tchannel = array of longint; - tchannels = array of tchannel; + tfloatlist = specialize tfpglist; + tspectrum = array of double; + tchannel = array of longint; + tchannels = array of tchannel; // ttrackchannel @@ -171,7 +171,7 @@ ttrackanalyzer = class(tthread) procedure readheader(astream: tstream); function readsamples(astream: tstream; achannels: tchannels; achannelsize: longint): longint; procedure readfromstream(astream: tstream); - procedure putfreq(achannel: tchannel; var spectrum: tspectrum; spectrumws: longint); + procedure getspectrum(achannel: plongint; count: longint; aspectrum: pfloat); function getrms2(achannel: tchannel; index, count: longint): double; function getpeak(achannel: tchannel; index, count: longint): double; function getrms(channel: word): double; @@ -383,34 +383,28 @@ procedure ttrackanalyzer.clear; fstatus := 0; end; -procedure ttrackanalyzer.putfreq(achannel: tchannel; var spectrum: tspectrum; spectrumws: longint); +procedure ttrackanalyzer.getspectrum(achannel: plongint; count: longint; aspectrum: pfloat); var - i, j, k, index: longint; - ibuff: tcompvector = nil; + i: longint; + buff: tcompvector = nil; freq: tcompvector; begin - j := 0; - index := 0; - setlength(ibuff, spectrumws); - for i := 0 to length(achannel) -1 do + setlength(buff, count); + for i := 0 to count -1 do begin - ibuff[j].x := (0.5-0.5*cos(2*pi*j/(spectrumws-1)))*achannel[i]; - ibuff[j].y := 0; - inc(j); + buff[i].x := (0.5-0.5*cos(2*pi*i/(count-1)))*achannel^; + buff[i].y := 0; + inc(achannel); + end; - if j = spectrumws then - begin - freq := FFT(j, ibuff); - for k := 0 to length(freq) div 2 -1 do - begin - spectrum[index] := 2*sqrt(sqr(freq[k].x) + sqr(freq[k].y))/spectrumws; - inc(index); - end; - freq := nil; - j := 0; - end; + freq := FFT(count, buff); + for i := 0 to (count div 2) -1 do + begin + aspectrum^ := 2*sqrt(sqr(freq[i].x) + sqr(freq[i].y))/count; + inc(aspectrum); end; - ibuff := nil; + freq := nil; + buff := nil; end; function ttrackanalyzer.getrms2(achannel: tchannel; index, count: longint): double; @@ -535,12 +529,13 @@ procedure ttrackanalyzer.execute; procedure ttrackanalyzer.readfromstream(astream:tstream); var - i, j: longint; + i, j, k: longint; blocksize: longword; blockcount: longword; - channels: tchannels = nil; - samples: longword; - seconds: longword; + samples: tchannels = nil; + samplecount: longword; + secondcount: longword; + step, steps: longint; begin {$ifopt D+} writeln('track.name ', ftrack.fname); @@ -564,8 +559,8 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); fnorm := 1 shl (ftrack.fbitspersample -1); if ftrack.fsamplerate > 0 then begin - seconds := (fdatachunk.subck2size div ffmt.blockalign) div ftrack.fsamplerate; - ftrack.fduration := format('%3.2d:%2.2d', [seconds div (60), seconds mod (60)]); + secondcount := (fdatachunk.subck2size div ffmt.blockalign) div ftrack.fsamplerate; + ftrack.fduration := format('%3.2d:%2.2d', [secondcount div (60), secondcount mod (60)]); end; // read samplecount if status <> 0 then exit; @@ -573,47 +568,64 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); for i := low(fdata) to high(fdata) do fdata[i] := ttrackchannel.create; - samples := fdatachunk.subck2size div ffmt.blockalign; - blocksize := trunc(132480 / 44100 * ffmt.samplespersec); - blockcount := 0; + samplecount := fdatachunk.subck2size div ffmt.blockalign; + blocksize := trunc(132480 / 44100 * ffmt.samplespersec); + blockcount := 0; if blocksize > 0 then - blockcount := samples div blocksize; + blockcount := samplecount div blocksize; if blockcount = 0 then fstatus := -3; if blocksize = 0 then fstatus := -3; if status <> 0 then exit; - // allocate track channels + // allocate track samples setlength(ftrack.fchannels, ffmt.channels); - // allocate track channels spectrum + // allocate track samples spectrum if fspectrumon then for i := 0 to length(ftrack.fchannels) -1 do - setlength(ftrack.fchannels[i].fspectrum, samples div 2); - // allocate channels buffer - setlength(channels, ffmt.channels); + setlength(ftrack.fchannels[i].fspectrum, samplecount div 2); + // allocate samples buffer + setlength(samples, ffmt.channels); for i := 0 to length(ftrack.fchannels) -1 do - setlength(channels[i], samples); - // read samples - readsamples(astream, channels, samples); + setlength(samples[i], samplecount); + // calculate steps + step := 1; + steps := blockcount; + if fspectrumon then + for i := 0 to ffmt.channels -1 do + steps := steps + samplecount div ftrack.fchannels[i].spectrumws; + // read samplescount + readsamples(astream, samples, samplecount); for i := 0 to blockcount -1 do begin - fpercentage := 100*(i + 1)/blockcount; + fpercentage := 100*step/steps; if assigned(fonprogress) then queue(fonprogress); + inc(step); // calculate block rms and peak for j := 0 to ffmt.channels -1 do begin - fdata[j].add(getrms2(channels[j], i * blocksize, blocksize), - getpeak(channels[j], i * blocksize, blocksize)); + fdata[j].add(getrms2(samples[j], i * blocksize, blocksize), + getpeak(samples[j], i * blocksize, blocksize)); end; end; // calculate spectrum if fspectrumon then for i := 0 to ffmt.channels -1 do - putfreq(channels[i], ftrack.fchannels[i].fspectrum, ftrack.fchannels[i].spectrumws); - // de-allocate channels buffer + for j := 0 to (samplecount div ftrack.fchannels[i].spectrumws) -1 do + begin + fpercentage := 100*step/steps; + if assigned(fonprogress) then + queue(fonprogress); + inc(step); + + k := j * ftrack.fchannels[i].spectrumws; + + getspectrum(@samples[i][k], ftrack.fchannels[i].spectrumws, @ftrack.fchannels[i].fspectrum[k div 2]); + end; + // de-allocate samples buffer for i := 0 to length(ftrack.fchannels) -1 do - setlength(channels[i], 0); - setlength(channels, 0); + setlength(samples[i], 0); + setlength(samples, 0); fpercentage := 100; if assigned(fonprogress) then From 50065115ade00ee7a4ab68e4f7bc1e7eb26fdbb3 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 1 Dec 2024 09:42:05 +0100 Subject: [PATCH 17/21] Updating spectrum tools --- src/mainfrm.lfm | 379 +++++++++++++++++++++++++++++++---------------- src/mainfrm.pas | 272 ++++++++++++++++++++++------------ src/soundwav.pas | 75 +++++++--- 3 files changed, 477 insertions(+), 249 deletions(-) diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index 789c197..e5cb7ad 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -1,11 +1,11 @@ object audiofrm: Taudiofrm - Left = 587 + Left = 958 Height = 571 - Top = 330 - Width = 700 + Top = 475 + Width = 714 Caption = 'AudioMeter 0.4.6 - Dynamic Range Meter' ClientHeight = 571 - ClientWidth = 700 + ClientWidth = 714 Color = clGray Constraints.MinHeight = 440 Constraints.MinWidth = 700 @@ -39,7 +39,7 @@ object audiofrm: Taudiofrm AnchorSideTop.Control = btnfolder AnchorSideRight.Control = btnfolder AnchorSideRight.Side = asrBottom - Left = 611 + Left = 625 Height = 32 Top = 34 Width = 32 @@ -85,7 +85,7 @@ object audiofrm: Taudiofrm AnchorSideTop.Side = asrBottom AnchorSideRight.Control = Owner AnchorSideRight.Side = asrBottom - Left = 656 + Left = 670 Height = 32 Top = 34 Width = 32 @@ -137,7 +137,7 @@ object audiofrm: Taudiofrm Color = clBlack ParentBackground = False ParentColor = False - TabOrder = 0 + TabOrder = 1 object BitsPanel: TPanel AnchorSideLeft.Control = DetailsPanel AnchorSideTop.Control = Bevel1 @@ -444,7 +444,7 @@ object audiofrm: Taudiofrm ClientHeight = 131 ClientWidth = 248 ParentBackground = False - TabOrder = 1 + TabOrder = 2 object drlb: TStaticText AnchorSideLeft.Control = DRPanel AnchorSideTop.Control = DRPanel @@ -538,20 +538,19 @@ object audiofrm: Taudiofrm Left = 3 Height = 344 Top = 222 - Width = 694 + Width = 711 PageIndex = 1 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 3 BorderSpacing.Top = 5 - BorderSpacing.Right = 3 BorderSpacing.Bottom = 5 - TabOrder = 2 + TabOrder = 0 object Page1: TPage object dbchart: TChart Left = 0 Height = 344 Top = 0 - Width = 694 + Width = 711 AllowPanning = False AllowZoom = False AxisList = < @@ -576,6 +575,7 @@ object audiofrm: Taudiofrm end item Grid.Visible = False + Intervals.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps] Alignment = calBottom Arrow.Visible = True Marks.LabelFont.Color = clWhite @@ -595,6 +595,7 @@ object audiofrm: Taudiofrm BackColor = clBlack Foot.Brush.Color = clBtnFace Foot.Font.Color = clBlue + Frame.Visible = False Title.Brush.Color = clBtnFace Title.Font.Color = clBlue Title.Text.Strings = ( @@ -602,7 +603,7 @@ object audiofrm: Taudiofrm ) Align = alClient Anchors = [] - Color = clBlack + ParentColor = True object rmseries: TBarSeries BarBrush.Color = clYellow Source = rms @@ -614,113 +615,130 @@ object audiofrm: Taudiofrm end end object Page2: TPage - object DurationPanel: TPanel - AnchorSideLeft.Control = Page2 - AnchorSideTop.Control = Page2 - AnchorSideBottom.Control = FrequencyPanel - Left = 10 - Height = 307 - Top = 6 - Width = 40 - Anchors = [akTop, akLeft, akBottom] - AutoSize = True - BorderSpacing.Left = 10 - BorderSpacing.Top = 6 - BorderSpacing.Bottom = 6 - BevelOuter = bvNone - Caption = 'Time' - ClientHeight = 307 - ClientWidth = 40 - Constraints.MinWidth = 40 - ParentBackground = False - TabOrder = 0 - object spectrumfirstvalue: TLabel - Left = 0 - Height = 19 - Top = 0 - Width = 40 - Align = alTop - Alignment = taRightJustify - Caption = '0:00s' - end - object spectrumsecondvalue: TLabel - Left = 0 - Height = 19 - Top = 288 - Width = 40 - Align = alBottom - Alignment = taRightJustify - Caption = '0:00s' - end - end - object FrequencyPanel: TPanel - AnchorSideTop.Side = asrBottom - AnchorSideRight.Control = Page2 - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = Page2 - AnchorSideBottom.Side = asrBottom - Left = 56 - Height = 19 - Top = 319 - Width = 612 - Anchors = [akLeft, akRight, akBottom] - AutoSize = True - BorderSpacing.Right = 26 - BorderSpacing.Bottom = 6 - BevelOuter = bvNone - Caption = 'Frequency' - ClientHeight = 19 - ClientWidth = 612 - ParentBackground = False - TabOrder = 1 - object frequencyfirstvalue: TLabel - Left = 0 - Height = 19 - Top = 0 - Width = 30 - Align = alLeft - Caption = '0 Hz' - Layout = tlCenter - end - object frequencysecondvalue: TLabel - Left = 567 - Height = 19 - Top = 0 - Width = 45 - Align = alRight - Caption = '25 kHz' - Layout = tlCenter - end + object spectrumscreen: TChart + Left = 0 + Height = 344 + Top = 0 + Width = 711 + AllowPanning = False + AllowZoom = False + AxisList = < + item + Grid.Visible = False + Marks.LabelFont.Color = clWhite + Marks.LabelFont.Height = -13 + Marks.LabelFont.Name = 'Sans' + Marks.LabelBrush.Style = bsClear + Minors = <> + Range.Max = 100 + Range.UseMax = True + Range.UseMin = True + Title.LabelFont.Color = clWhite + Title.LabelFont.Height = -13 + Title.LabelFont.Name = 'Sans' + Title.LabelFont.Orientation = 900 + Title.LabelFont.Style = [fsBold] + Title.Visible = True + Title.Caption = 'dB' + Title.LabelBrush.Style = bsClear + end + item + Grid.Visible = False + Intervals.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps] + Alignment = calBottom + Arrow.Visible = True + Marks.LabelFont.Color = clWhite + Marks.LabelBrush.Style = bsClear + Minors = <> + Range.Max = 22050 + Range.UseMax = True + Range.UseMin = True + Title.LabelFont.Color = clWhite + Title.LabelFont.Height = -13 + Title.LabelFont.Name = 'Sans' + Title.LabelFont.Style = [fsBold] + Title.Visible = True + Title.Caption = 'Frequency (Hz)' + Title.LabelBrush.Style = bsClear + end> + BackColor = clBlack + Foot.Brush.Color = clBtnFace + Foot.Font.Color = clBlue + Frame.Visible = False + Title.Brush.Color = clBtnFace + Title.Font.Color = clBlue + Title.Text.Strings = ( + '' + ) + OnBeforeCustomDrawBackWall = spectrumscreenBeforeCustomDrawBackWall + Align = alClient + Anchors = [] + ParentColor = True end - object SpectrumPanel: TPanel - AnchorSideLeft.Control = DurationPanel - AnchorSideLeft.Side = asrBottom - AnchorSideTop.Control = DurationPanel - AnchorSideRight.Control = FrequencyPanel - AnchorSideRight.Side = asrBottom - AnchorSideBottom.Control = DurationPanel - AnchorSideBottom.Side = asrBottom - Left = 56 - Height = 307 - Top = 6 - Width = 612 - Anchors = [akTop, akLeft, akRight, akBottom] - BorderSpacing.Left = 6 - BevelOuter = bvNone - ClientHeight = 307 - ClientWidth = 612 - ParentBackground = False - TabOrder = 2 - object SpectrumImage: TBGRAVirtualScreen - Left = 0 - Height = 307 - Top = 0 - Width = 612 - OnRedraw = SpectrumImageRedraw - BitmapAutoScale = False - Align = alClient - Alignment = taLeftJustify - TabOrder = 0 + end + object Page3: TPage + object spectrumchart: TChart + Left = 0 + Height = 344 + Top = 0 + Width = 711 + AllowPanning = False + AllowZoom = False + AxisList = < + item + Grid.Visible = False + Marks.LabelFont.Color = clWhite + Marks.LabelFont.Height = -13 + Marks.LabelFont.Name = 'Sans' + Marks.LabelBrush.Style = bsClear + Minors = <> + Range.Max = 100 + Range.UseMax = True + Range.UseMin = True + Title.LabelFont.Color = clWhite + Title.LabelFont.Height = -13 + Title.LabelFont.Name = 'Sans' + Title.LabelFont.Orientation = 900 + Title.LabelFont.Style = [fsBold] + Title.Visible = True + Title.Caption = 'dB' + Title.LabelBrush.Style = bsClear + end + item + Grid.Visible = False + Intervals.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps] + Alignment = calBottom + Arrow.Visible = True + Marks.LabelFont.Color = clWhite + Marks.LabelBrush.Style = bsClear + Minors = <> + Range.Max = 22050 + Range.UseMax = True + Range.UseMin = True + Title.LabelFont.Color = clWhite + Title.LabelFont.Height = -13 + Title.LabelFont.Name = 'Sans' + Title.LabelFont.Style = [fsBold] + Title.Visible = True + Title.Caption = 'Frequency (Hz)' + Title.LabelBrush.Style = bsClear + end> + BackColor = clBlack + Foot.Brush.Color = clBtnFace + Foot.Font.Color = clBlue + Frame.Visible = False + Title.Brush.Color = clBtnFace + Title.Font.Color = clBlue + Title.Text.Strings = ( + '' + ) + Align = alClient + Anchors = [] + ParentColor = True + object spectrumseries: TBarSeries + BarBrush.Color = clRed + Source = freq + StackedNaN = snDoNotDraw end end end @@ -732,7 +750,7 @@ object audiofrm: Taudiofrm Left = 26 Height = 32 Top = 185 - Width = 120 + Width = 140 StateClicked.Background.Gradient1.StartColor = 8404992 StateClicked.Background.Gradient1.EndColor = 4194304 StateClicked.Background.Gradient1.GradientType = gtRadial @@ -823,14 +841,113 @@ object audiofrm: Taudiofrm MemoryUsage = bmuHigh end object SpectrumBtn: TBCButton + AnchorSideLeft.Control = SpectrAnalisysBtn + AnchorSideLeft.Side = asrBottom + AnchorSideTop.Control = DetailsPanel + AnchorSideTop.Side = asrBottom + Left = 326 + Height = 32 + Top = 185 + Width = 140 + StateClicked.Background.Gradient1.StartColor = 8404992 + StateClicked.Background.Gradient1.EndColor = 4194304 + StateClicked.Background.Gradient1.GradientType = gtRadial + StateClicked.Background.Gradient1.Point1XPercent = 50 + StateClicked.Background.Gradient1.Point1YPercent = 100 + StateClicked.Background.Gradient1.Point2XPercent = 0 + StateClicked.Background.Gradient1.Point2YPercent = 0 + StateClicked.Background.Gradient2.StartColor = clWhite + StateClicked.Background.Gradient2.EndColor = clBlack + StateClicked.Background.Gradient2.GradientType = gtLinear + StateClicked.Background.Gradient2.Point1XPercent = 0 + StateClicked.Background.Gradient2.Point1YPercent = 0 + StateClicked.Background.Gradient2.Point2XPercent = 0 + StateClicked.Background.Gradient2.Point2YPercent = 100 + StateClicked.Background.Gradient1EndPercent = 100 + StateClicked.Background.Style = bbsColor + StateClicked.Border.Style = bboNone + StateClicked.FontEx.Color = 16770790 + StateClicked.FontEx.FontQuality = fqSystemClearType + StateClicked.FontEx.Shadow = True + StateClicked.FontEx.ShadowRadius = 2 + StateClicked.FontEx.ShadowOffsetX = 1 + StateClicked.FontEx.ShadowOffsetY = 1 + StateClicked.FontEx.Style = [fsBold] + StateHover.Background.Gradient1.StartColor = 16744448 + StateHover.Background.Gradient1.EndColor = 8404992 + StateHover.Background.Gradient1.GradientType = gtRadial + StateHover.Background.Gradient1.Point1XPercent = 50 + StateHover.Background.Gradient1.Point1YPercent = 100 + StateHover.Background.Gradient1.Point2XPercent = 0 + StateHover.Background.Gradient1.Point2YPercent = 0 + StateHover.Background.Gradient2.StartColor = clWhite + StateHover.Background.Gradient2.EndColor = clBlack + StateHover.Background.Gradient2.GradientType = gtLinear + StateHover.Background.Gradient2.Point1XPercent = 0 + StateHover.Background.Gradient2.Point1YPercent = 0 + StateHover.Background.Gradient2.Point2XPercent = 0 + StateHover.Background.Gradient2.Point2YPercent = 100 + StateHover.Background.Gradient1EndPercent = 100 + StateHover.Background.Style = bbsColor + StateHover.Border.Color = clWhite + StateHover.Border.Style = bboSolid + StateHover.FontEx.Color = clWhite + StateHover.FontEx.FontQuality = fqSystemClearType + StateHover.FontEx.Shadow = True + StateHover.FontEx.ShadowRadius = 2 + StateHover.FontEx.ShadowOffsetX = 1 + StateHover.FontEx.ShadowOffsetY = 1 + StateHover.FontEx.Style = [fsBold] + StateNormal.Background.Gradient1.StartColor = 4194304 + StateNormal.Background.Gradient1.EndColor = 8405056 + StateNormal.Background.Gradient1.GradientType = gtLinear + StateNormal.Background.Gradient1.Point1XPercent = 0 + StateNormal.Background.Gradient1.Point1YPercent = 0 + StateNormal.Background.Gradient1.Point2XPercent = 0 + StateNormal.Background.Gradient1.Point2YPercent = 100 + StateNormal.Background.Gradient2.StartColor = 8405056 + StateNormal.Background.Gradient2.EndColor = 4194304 + StateNormal.Background.Gradient2.GradientType = gtRadial + StateNormal.Background.Gradient2.Point1XPercent = 50 + StateNormal.Background.Gradient2.Point1YPercent = 100 + StateNormal.Background.Gradient2.Point2XPercent = 0 + StateNormal.Background.Gradient2.Point2YPercent = 0 + StateNormal.Background.Gradient1EndPercent = 60 + StateNormal.Background.Style = bbsColor + StateNormal.Border.Color = clWhite + StateNormal.Border.Style = bboSolid + StateNormal.FontEx.Color = 16770790 + StateNormal.FontEx.FontQuality = fqSystemClearType + StateNormal.FontEx.Shadow = False + StateNormal.FontEx.ShadowRadius = 2 + StateNormal.FontEx.ShadowOffsetX = 1 + StateNormal.FontEx.ShadowOffsetY = 1 + StateNormal.FontEx.Style = [fsBold] + BorderSpacing.Left = 10 + BorderSpacing.Top = 25 + Caption = 'Spectrogram' + Color = clNone + DropDownWidth = 16 + DropDownArrowSize = 8 + GlobalOpacity = 255 + OnClick = BlocksBtnClick + ParentColor = False + Rounding.RoundX = 0 + Rounding.RoundY = 0 + RoundingDropDown.RoundX = 1 + RoundingDropDown.RoundY = 1 + TextApplyGlobalOpacity = False + MemoryUsage = bmuHigh + end + object SpectrAnalisysBtn: TBCButton AnchorSideLeft.Control = BlocksBtn AnchorSideLeft.Side = asrBottom AnchorSideTop.Control = DetailsPanel AnchorSideTop.Side = asrBottom - Left = 156 + Left = 176 Height = 32 Top = 185 - Width = 120 + Width = 140 StateClicked.Background.Gradient1.StartColor = 8404992 StateClicked.Background.Gradient1.EndColor = 4194304 StateClicked.Background.Gradient1.GradientType = gtRadial @@ -900,14 +1017,14 @@ object audiofrm: Taudiofrm StateNormal.Border.Style = bboSolid StateNormal.FontEx.Color = 16770790 StateNormal.FontEx.FontQuality = fqSystemClearType - StateNormal.FontEx.Shadow = True + StateNormal.FontEx.Shadow = False StateNormal.FontEx.ShadowRadius = 2 StateNormal.FontEx.ShadowOffsetX = 1 StateNormal.FontEx.ShadowOffsetY = 1 StateNormal.FontEx.Style = [fsBold] BorderSpacing.Left = 10 BorderSpacing.Top = 25 - Caption = 'Spectrum' + Caption = 'Spectral analysis' Color = clNone DropDownWidth = 16 DropDownArrowSize = 8 @@ -922,20 +1039,20 @@ object audiofrm: Taudiofrm MemoryUsage = bmuHigh end object filedialog: TOpenDialog - Left = 504 + Left = 512 Top = 16 end object rms: TListChartSource SortDir = sdDescending - Left = 552 + Left = 576 Top = 16 end object peak: TListChartSource - Left = 552 + Left = 576 Top = 80 end object dirdialog: TSelectDirectoryDialog - Left = 504 + Left = 512 Top = 80 end object buttons: TImageList @@ -999,10 +1116,14 @@ object audiofrm: Taudiofrm 6FB457 } end - object Report: TImageList + object report: TImageList Height = 146 Width = 446 Left = 448 Top = 80 end + object freq: TListChartSource + Left = 576 + Top = 144 + end end diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 3202035..517451d 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -28,56 +28,54 @@ interface uses classes, sysutils, forms, controls, graphics, dialogs, buttons, stdctrls, extctrls, comctrls, tagraph, taseries, tasources, bufstream, soundwav, - bcradialprogressbar, bclistbox, bcbutton, bgravirtualscreen, process, - inifiles, bgrabitmap, bgrabitmaptypes, bctypes; + bcradialprogressbar, bclistbox, bcbutton, process, inifiles, bgrabitmap, + bgrabitmaptypes, bctypes, tadrawutils; type { taudiofrm } taudiofrm = class(tform) - BlocksBtn: TBCButton; - FrequencyPanel: TPanel; - frequencyfirstvalue: TLabel; - frequencysecondvalue: TLabel; - SpectrumImage: TBGRAVirtualScreen; - SpectrumPanel: TPanel; - spectrumsecondvalue: TLabel; - spectrumfirstvalue: TLabel; - DurationPanel: TPanel; - SpectrumBtn: TBCButton; - Bevel1: TBevel; - Bevel2: TBevel; - Bevel3: TBevel; - bit16: TLabel; - bit24: TLabel; - bit8: TLabel; + blocksbtn: tbcbutton; + freq: tlistchartsource; + spectrumchart: tchart; + page3: tpage; + spectranalisysbtn: tbcbutton; + spectrumscreen: TChart; + spectrumseries: tbarseries; + spectrumbtn: tbcbutton; + bevel1: tbevel; + bevel2: tbevel; + bevel3: tbevel; + bit16: tlabel; + bit24: tlabel; + bit8: tlabel; btnfile: timage; btnfolder: timage; buttons: timagelist; - dbchart: TChart; - drlb: TStaticText; - drvalue: TStaticText; - khz176: TLabel; - khz192: TLabel; - khz44: TLabel; - khz48: TLabel; - khz88: TLabel; - khz96: TLabel; - DetailsPanel: TPanel; - BitsPanel: TPanel; - LeftHzPanel: TPanel; - mono: TLabel; - DRPanel: TPanel; - Notebook: TNotebook; - Page1: TPage; - Page2: TPage; - peakseries: TBarSeries; - progressbar: TBCRadialProgressBar; - progresspanel: TPanel; - RightHzPanel: TPanel; - rmseries: TBarSeries; - stereo: TLabel; - Report: TImageList; + dbchart: tchart; + drlb: tstatictext; + drvalue: tstatictext; + khz176: tlabel; + khz192: tlabel; + khz44: tlabel; + khz48: tlabel; + khz88: tlabel; + khz96: tlabel; + detailspanel: tpanel; + bitspanel: tpanel; + lefthzpanel: tpanel; + mono: tlabel; + drpanel: tpanel; + notebook: tnotebook; + page1: tpage; + page2: tpage; + peakseries: tbarseries; + progressbar: tbcradialprogressbar; + progresspanel: tpanel; + righthzpanel: tpanel; + rmseries: tbarseries; + stereo: tlabel; + report: timagelist; audio: tlabel; dirdialog: tselectdirectorydialog; peak: tlistchartsource; @@ -103,9 +101,11 @@ taudiofrm = class(tform) procedure onprogress; procedure clear; procedure execute; - function drawspectrum(atrack: ttrack): tbgrabitmap; + procedure updatespectrumchart(atrack: ttrack); + function drawspectrum(atrack: ttrack; awidth, aheight: longint): tbgrabitmap; procedure btnloadicon(btn: timage; index: longint; x, y: longint); - procedure SpectrumImageRedraw(Sender: TObject; Bitmap: TBGRABitmap); + procedure spectrumscreenbeforecustomdrawbackwall(asender: tchart; + adrawer: ichartdrawer; const arect: trect; var adodefaultdrawing: boolean); private buffer: treadbufstream; stream: tfilestream; @@ -150,22 +150,23 @@ procedure taudiofrm.formcreate(sender: tobject); // inizialize mail form color := clblack; // initialize buttons - blocksbtn .statenormal .fontex.shadow := false; - blocksbtn .statehover .fontex.shadow := false; - blocksbtn .stateclicked.fontex.shadow := false; + blocksbtn.statenormal .fontex.shadow := false; + blocksbtn.statehover .fontex.shadow := false; + blocksbtn.stateclicked.fontex.shadow := false; spectrumbtn.statenormal .fontex.shadow := false; spectrumbtn.statehover .fontex.shadow := false; spectrumbtn.stateclicked.fontex.shadow := false; + spectranalisysbtn.statenormal .fontex.shadow := false; + spectranalisysbtn.statehover .fontex.shadow := false; + spectranalisysbtn.stateclicked.fontex.shadow := false; // initialize notebook notebook.pageindex := 0; - // initialize spectrumimage - spectrumimage.parentcolor := true; // inizialize working := false; clear; end; -function cutoff(const S: string): string; +function cutoff(const s: string): string; begin result := s; setlength(result, max(0, length(result) - 4)); @@ -179,11 +180,11 @@ procedure taudiofrm.formdestroy(sender: tobject); tracklist.destroy; end; -procedure taudiofrm.formresize(Sender: TObject); +procedure taudiofrm.formresize(sender: tobject); begin - while (audio.Left + audio.Width) > (btnFolder.Left + btnFolder.Width) do + while (audio.left + audio.width) > (btnfolder.left + btnfolder.width) do begin - audio.Caption := cutoff(audio.Caption); + audio.caption := cutoff(audio.caption); end; end; @@ -199,9 +200,9 @@ procedure taudiofrm.onstart; working := true; audio.font.color := clwhite; audio.caption := extractfilename(tracklist.tracks[trackindex].name); - while (audio.Left + audio.Width) > (btnFolder.Left + btnFolder.Width) do + while (audio.left + audio.width) > (btnfolder.left + btnfolder.width) do begin - audio.Caption := cutoff(audio.Caption); + audio.caption := cutoff(audio.caption); end; btnfile .enabled := false; @@ -232,10 +233,10 @@ procedure taudiofrm.onstop; trackindex := tracklist.count; audio.font.color := clred; case wave.status of - -1: audio.caption := 'File format error!'; - -2: audio.caption := 'File is empty!'; - -3: audio.caption := 'File is too short!'; - else audio.caption := 'Unknown error!'; + -1: audio.caption := 'file format error!'; + -2: audio.caption := 'file is empty!'; + -3: audio.caption := 'file is too short!'; + else audio.caption := 'unknown error!'; end; btnfile.enabled := true; btnfolder.enabled := true; @@ -254,7 +255,7 @@ procedure taudiofrm.onstop; rmsi := 0; for j := 0 to track.channelcount -1 do rmsi := rmsi + sqrt(track.channels[j].rms2[i]); - rms.add(i, max(0, db(rmsi/track.channelcount*norm))); + rms.add(i, max(0, db(rmsi/track.channelcount*norm))); peaki := 0; for j := 0 to track.channelcount -1 do @@ -265,6 +266,10 @@ procedure taudiofrm.onstop; dbchart.bottomaxis.range.max := rms.count; dbchart.invalidate; + spectrumscreen.bottomaxis.range.max := track.samplerate div 2; + spectrumscreen.invalidate; + + bit8 .font.color := clgray; if track.bitspersample = 8 then bit8 .font.color := clwhite; bit16 .font.color := clgray; if track.bitspersample = 16 then bit16 .font.color := clwhite; bit24 .font.color := clgray; if track.bitspersample = 24 then bit24 .font.color := clwhite; @@ -300,6 +305,10 @@ procedure taudiofrm.onstop; if drvalue.caption = '13' then drvalue.font.color := rgbtocolor( 72, 255, 0) else drvalue.font.color := rgbtocolor( 0, 255, 0); end; + // load spectrum chart + updatespectrumchart(track); + + // drvalue .visible := true; btnfile .enabled := true; btnfolder .enabled := true; @@ -341,7 +350,7 @@ procedure taudiofrm.onstop; working := false; // update spectrum image if notebook.pageindex = 1 then - spectrumimage.redrawbitmap; + spectrumscreen.invalidate; end; execute; end; @@ -549,45 +558,94 @@ function getcolor(factor: double): tbgrapixel; result.alpha := 255; end; -function taudiofrm.drawspectrum(atrack: ttrack): tbgrabitmap; +function taudiofrm.drawspectrum(atrack: ttrack; awidth, aheight: longint): tbgrabitmap; var - i, j, k: longint; - z: double; index: longint; + i, j, k: longint; + amp, maxamp: double; windowsize: longint; windowcount: longint; begin result := tbgrabitmap.create; - result.setsize(spectrumpanel.width, spectrumpanel.height); + result.setsize(awidth, aheight); result.filltransparent; if atrack.channelcount > 0 then begin - for i := 0 to result.width -1 do - for j := 0 to result.height -1 do + maxamp := 0; + for i := 0 to atrack.channelcount -1 do + for j := 0 to length(atrack.channels[i].spectrum) -1 do begin - z := 0; + maxamp := max(maxamp, atrack.channels[i].spectrum[j]); + end; + + windowsize := atrack.spectrumws div 2; + for i := 0 to awidth -1 do + for j := 0 to aheight -1 do + begin + amp := 0; for k := 0 to atrack.channelcount -1 do begin - windowsize := atrack.channels[k].spectrumws div 2; windowcount := length(atrack.channels[k].spectrum) div windowsize; - index := trunc((j+1)/result.height*(windowcount -1))*windowsize + trunc(i/result.width*windowsize); - - z := z + db(2*atrack.channels[k].spectrum[index])/db(1 shl atrack.bitspersample); + index := trunc(j/aheight*windowcount)*windowsize + trunc(i/awidth*windowsize); + // skip DC component + if index mod windowsize <> 0 then + begin + amp := amp + db(atrack.maxamp*atrack.channels[k].spectrum[index]/maxamp)/db(1 shl atrack.bitspersample); + end; end; - result.setpixel(i, j, getcolor(z/atrack.channelcount)); + result.setpixel(i, j, getcolor(amp/atrack.channelcount)); end; end; +end; + +procedure taudiofrm.updatespectrumchart(atrack: ttrack); +var + i, j, k: longint; + maxamp: double; + windowsize: longint; + windowcount: longint; + arr: array of double = nil; +begin + freq.clear; + if atrack.channelcount > 0 then + begin + maxamp := minfloat; + for i := 0 to atrack.channelcount -1 do + for j := 0 to length(atrack.channels[i].spectrum) -1 do + begin + maxamp := max(maxamp, atrack.channels[i].spectrum[j]); + end; + + setlength(arr, atrack.spectrumws div 2); + for i := 0 to length(arr) -1 do arr[i] := 0; + + windowsize := atrack.spectrumws div 2; + for i := 0 to atrack.channelcount -1 do + begin + windowcount := length(atrack.channels[i].spectrum) div windowsize; - spectrumfirstvalue .caption := '0:00s'; - if atrack.duration = '' then - spectrumsecondvalue .caption := '0:00s' - else - spectrumsecondvalue .caption := atrack.duration + 's'; + for j := 0 to length(arr) -1 do + begin + arr[j] := 0; + if j mod windowsize <> 0 then + begin + for k := 0 to windowcount -1 do + begin + arr[j] := max(arr[j], atrack.channels[i].spectrum[k*windowsize + j]/maxamp); + end; + end; + end; + end; - frequencyfirstvalue .caption := '0 Hz'; - frequencysecondvalue.caption := (atrack.samplerate div 2).tostring + ' Hz'; + for i := 0 to length(arr) -1 do + if i mod windowsize <> 0 then + begin + freq.add(i*atrack.samplerate/atrack.spectrumws, db(atrack.maxamp*arr[i]/atrack.channelcount), '', clYellow); + end; + arr := nil; + end; end; procedure taudiofrm.btnfileclick(sender: tobject); @@ -643,6 +701,7 @@ procedure taudiofrm.blocksbtnclick(sender: tobject); var btn1: tbcbutton; btn2: tbcbutton; + btn3: tbcbutton; begin if sender = nil then begin @@ -650,21 +709,40 @@ procedure taudiofrm.blocksbtnclick(sender: tobject); begin btn1 := blocksbtn; btn2 := spectrumbtn; + btn3 := spectranalisysbtn; end else + if notebook.pageindex = 1 then begin + btn2 := blocksbtn; btn1 := spectrumbtn; + btn3 := spectranalisysbtn; + end else + begin btn2 := blocksbtn; + btn3 := spectrumbtn; + btn1 := spectranalisysbtn; end; end else begin btn1 := sender as tbcbutton; if btn1 = blocksbtn then - btn2 := spectrumbtn - else + begin + btn2 := spectrumbtn; + btn3 := spectranalisysbtn; + end else + if btn1 = spectrumbtn then + begin + btn2 := blocksbtn; + btn3 := spectranalisysbtn; + end else + begin btn2 := blocksbtn; + btn3 := spectrumbtn; + end; - if btn1 = blocksbtn then notebook.pageindex := 0 else - if btn1 = spectrumbtn then notebook.pageindex := 1; + if btn1 = blocksbtn then notebook.pageindex := 0 else + if btn1 = spectrumbtn then notebook.pageindex := 1 else + if btn1 = spectranalisysbtn then notebook.pageindex := 2; end; btn1.statenormal .background.color := clwhite; @@ -683,9 +761,13 @@ procedure taudiofrm.blocksbtnclick(sender: tobject); btn2.statehover .fontex .color := clwhite; btn2.stateclicked.fontex .color := clblack; - // update spectrum image - if notebook.pageindex = 1 then - spectrumimage.redrawbitmap; + btn3.statenormal .background.color := clblack; + btn3.statehover .background.color := clblack; + btn3.stateclicked.background.color := clwhite; + + btn3.statenormal .fontex .color := clwhite; + btn3.statehover .fontex .color := clwhite; + btn3.stateclicked.fontex .color := clblack; end; procedure taudiofrm.btnfilemouseup(sender: tobject; button: tmousebutton; @@ -745,21 +827,17 @@ procedure taudiofrm.btnloadicon(btn: timage; index: longint; x, y: longint); buttons.draw(btn.canvas, x, y, index); end; -procedure taudiofrm.spectrumimageredraw(sender: tobject; bitmap: tbgrabitmap); +procedure taudiofrm.spectrumscreenBeforeCustomDrawBackWall(asender: tchart; + adrawer: ichartdrawer; const arect: trect; var adodefaultdrawing: boolean); var bit: tbgrabitmap; begin + adodefaultdrawing := true; if (working = false) and (tracklist.count > 0) then begin - bit := tbgrabitmap.create; - bit := drawspectrum(tracklist[tracklist.count -1]); - bitmap.putimage(0, 0, bit, dmset, 255); - bit.destroy; - end else - begin - bit := tbgrabitmap.create; - bit.setsize(spectrumimage.width, spectrumimage.height); - bit.filltransparent; + adodefaultdrawing := false; + bit := drawspectrum(tracklist[tracklist.count -1], arect.width, arect.height); + adrawer.putimage(arect.left, arect.top, bit); bit.destroy; end; end; diff --git a/src/soundwav.pas b/src/soundwav.pas index 2cbdff5..58c9448 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -93,7 +93,6 @@ ttrackchannel = class frms2: tfloatlist; fpeak: tfloatlist; fspectrum: tspectrum; - fspectrumws: longint; function getrms2(index: longint): double; function getpeak(index: longint): double; function getcount: longint; @@ -103,7 +102,6 @@ ttrackchannel = class procedure add(const rms2i, peaki: double); public property spectrum: tspectrum read fspectrum; - property spectrumws: longint read fspectrumws; property rms2[index: longint]: double read getrms2; property peak[index: longint]: double read getpeak; property count: longint read getcount; @@ -127,7 +125,9 @@ ttrack = class frms: double; fpeak: double; fdr: double; + fmaxamp: double; fduration: string; + fspectrumws: longint; procedure setchannelcount(value: longint); function getchannel(index: longint): ttrackchannel; function getchannelcount: longint; @@ -144,9 +144,11 @@ ttrack = class property rms: double read frms; property peak: double read fpeak; property dr: double read fdr; + property maxamp: double read fmaxamp; property duration: string read fduration; property channels[index: longint]: ttrackchannel read getchannel; property channelcount: longint read getchannelcount write setchannelcount; + property spectrumws: longint read fspectrumws; end; { ttrackanalyzer } @@ -165,7 +167,7 @@ ttrackanalyzer = class(tthread) fonstart: tthreadmethod; fonstop: tthreadmethod; fonprogress: tthreadmethod; - fnorm: longint; + fnorm: double; fspectrumon: boolean; function getpercentage: longint; procedure readheader(astream: tstream); @@ -174,6 +176,7 @@ ttrackanalyzer = class(tthread) procedure getspectrum(achannel: plongint; count: longint; aspectrum: pfloat); function getrms2(achannel: tchannel; index, count: longint): double; function getpeak(achannel: tchannel; index, count: longint): double; + function getmaxamp(achannel: tchannel; index, count: longint): double; function getrms(channel: word): double; function getpeak(channel: word): double; function getdr(channel: word): double; @@ -254,7 +257,7 @@ function comparetrackname(item1, item2: pointer): longint; function db(const value: double): double; begin - if value > 0 then + if value > 1.0 then result := 20*log10(value) else result := 0; @@ -267,7 +270,6 @@ constructor ttrackchannel.create; inherited create; frms2 := tfloatlist.create; fpeak := tfloatlist.create; - fspectrumws := 1024; end; destructor ttrackchannel.destroy; @@ -313,6 +315,8 @@ constructor ttrack.create(const filename: string); frms := 0; fpeak := 0; fdr := 0; + fmaxamp := 0; + fspectrumws := 1024; end; destructor ttrack.destroy; @@ -387,24 +391,29 @@ procedure ttrackanalyzer.getspectrum(achannel: plongint; count: longint; aspectr var i: longint; buff: tcompvector = nil; - freq: tcompvector; + freq: tcompvector = nil; begin - setlength(buff, count); - for i := 0 to count -1 do + if count > 0 then begin - buff[i].x := (0.5-0.5*cos(2*pi*i/(count-1)))*achannel^; - buff[i].y := 0; - inc(achannel); - end; + setlength(buff, count); + for i := 0 to count -1 do + begin + buff[i].x := (0.5-0.5*cos(2*pi*i/(count-1)))*achannel^; + buff[i].y := 0; + inc(achannel); + end; - freq := FFT(count, buff); - for i := 0 to (count div 2) -1 do - begin - aspectrum^ := 2*sqrt(sqr(freq[i].x) + sqr(freq[i].y))/count; + freq := FFT(count, buff); + aspectrum^ := abs(freq[0].x)/count; inc(aspectrum); + for i := 1 to (count div 2) -1 do + begin + aspectrum^ := 2*sqrt(sqr(freq[i].x) + sqr(freq[i].y))/count; + inc(aspectrum); + end; + freq := nil; + buff := nil; end; - freq := nil; - buff := nil; end; function ttrackanalyzer.getrms2(achannel: tchannel; index, count: longint): double; @@ -430,6 +439,22 @@ function ttrackanalyzer.getpeak(achannel: tchannel; index, count: longint): doub end; end; +function ttrackanalyzer.getmaxamp(achannel: tchannel; index, count: longint): double; +var + i: longint; + zmin, zmax: float; +begin + result := 0; + zmin := maxfloat; + zmax := minfloat; + for i := index to (index + count) -1 do + begin + zmin := min(zmin, achannel[i]); + zmax := max(zmax, achannel[i]); + end; + result := zmax - zmin; +end; + function ttrackanalyzer.getdr(channel: word): double; var i, n: longint; @@ -554,6 +579,7 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); ftrack.frms := 0; ftrack.fpeak := 0; ftrack.fdr := 0; + ftrack.fmaxamp := 0; ftrack.fduration := '00:00'; fnorm := 1 shl (ftrack.fbitspersample -1); @@ -592,7 +618,7 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); steps := blockcount; if fspectrumon then for i := 0 to ffmt.channels -1 do - steps := steps + samplecount div ftrack.fchannels[i].spectrumws; + steps := steps + samplecount div ftrack.spectrumws; // read samplescount readsamples(astream, samples, samplecount); for i := 0 to blockcount -1 do @@ -601,26 +627,29 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); if assigned(fonprogress) then queue(fonprogress); inc(step); - // calculate block rms and peak + // calculate block rms and peak and db for j := 0 to ffmt.channels -1 do begin fdata[j].add(getrms2(samples[j], i * blocksize, blocksize), getpeak(samples[j], i * blocksize, blocksize)); + + ftrack.fmaxamp := max(ftrack.fmaxamp, getmaxamp(samples[j], i * blocksize, blocksize)); end; end; + // calculate spectrum if fspectrumon then for i := 0 to ffmt.channels -1 do - for j := 0 to (samplecount div ftrack.fchannels[i].spectrumws) -1 do + for j := 0 to (samplecount div ftrack.spectrumws) -1 do begin fpercentage := 100*step/steps; if assigned(fonprogress) then queue(fonprogress); inc(step); - k := j * ftrack.fchannels[i].spectrumws; + k := j * ftrack.spectrumws; - getspectrum(@samples[i][k], ftrack.fchannels[i].spectrumws, @ftrack.fchannels[i].fspectrum[k div 2]); + getspectrum(@samples[i][k], ftrack.spectrumws, @ftrack.fchannels[i].fspectrum[k div 2]); end; // de-allocate samples buffer for i := 0 to length(ftrack.fchannels) -1 do From 4000ec2743ed2f5abb2695158e0a7b69d1cabacf Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 1 Dec 2024 10:18:04 +0100 Subject: [PATCH 18/21] Adding bgra chartguiconnector --- audiometer.lpi | 3 +++ audiometer.lpr | 2 +- src/mainfrm.lfm | 5 +++++ src/mainfrm.pas | 5 +++-- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/audiometer.lpi b/audiometer.lpi index 096f0e6..8f65cdd 100644 --- a/audiometer.lpi +++ b/audiometer.lpi @@ -76,6 +76,9 @@ + + + diff --git a/audiometer.lpr b/audiometer.lpr index 6f9c1a4..e599d2e 100644 --- a/audiometer.lpr +++ b/audiometer.lpr @@ -25,7 +25,7 @@ uses {$IFDEF UNIX} cthreads, {$ENDIF} interfaces, - forms, mainfrm, tachartlazaruspkg; + forms, mainfrm, tachartlazaruspkg, tachartbgra; {$R *.res} diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index e5cb7ad..217b4df 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -665,6 +665,7 @@ object audiofrm: Taudiofrm Foot.Brush.Color = clBtnFace Foot.Font.Color = clBlue Frame.Visible = False + GUIConnector = guiconnector Title.Brush.Color = clBtnFace Title.Font.Color = clBlue Title.Text.Strings = ( @@ -1123,6 +1124,10 @@ object audiofrm: Taudiofrm Top = 80 end object freq: TListChartSource + Left = 624 + Top = 80 + end + object guiconnector: TChartGUIConnectorBGRA Left = 576 Top = 144 end diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 517451d..fb45f2a 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -29,13 +29,14 @@ interface classes, sysutils, forms, controls, graphics, dialogs, buttons, stdctrls, extctrls, comctrls, tagraph, taseries, tasources, bufstream, soundwav, bcradialprogressbar, bclistbox, bcbutton, process, inifiles, bgrabitmap, - bgrabitmaptypes, bctypes, tadrawutils; + bgrabitmaptypes, bctypes, tadrawutils, taguiconnectorbgra; type { taudiofrm } taudiofrm = class(tform) blocksbtn: tbcbutton; + guiconnector: TChartGUIConnectorBGRA; freq: tlistchartsource; spectrumchart: tchart; page3: tpage; @@ -127,7 +128,7 @@ implementation {$R *.lfm} uses - math; + tadrawerbgra, math; { taudiofrm } From 1fe27c042024ab715160b846f37174019d6afe1d Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 1 Dec 2024 10:39:58 +0100 Subject: [PATCH 19/21] Removing bgra connector --- audiometer.lpi | 3 --- audiometer.lpr | 2 +- src/mainfrm.lfm | 18 +++++++++--------- src/mainfrm.pas | 5 ++--- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/audiometer.lpi b/audiometer.lpi index 8f65cdd..096f0e6 100644 --- a/audiometer.lpi +++ b/audiometer.lpi @@ -76,9 +76,6 @@ - - - diff --git a/audiometer.lpr b/audiometer.lpr index e599d2e..6f9c1a4 100644 --- a/audiometer.lpr +++ b/audiometer.lpr @@ -25,7 +25,7 @@ uses {$IFDEF UNIX} cthreads, {$ENDIF} interfaces, - forms, mainfrm, tachartlazaruspkg, tachartbgra; + forms, mainfrm, tachartlazaruspkg; {$R *.res} diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index 217b4df..61550e8 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -539,7 +539,7 @@ object audiofrm: Taudiofrm Height = 344 Top = 222 Width = 711 - PageIndex = 1 + PageIndex = 2 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 3 BorderSpacing.Top = 5 @@ -550,7 +550,7 @@ object audiofrm: Taudiofrm Left = 0 Height = 344 Top = 0 - Width = 711 + Width = 705 AllowPanning = False AllowZoom = False AxisList = < @@ -603,6 +603,7 @@ object audiofrm: Taudiofrm ) Align = alClient Anchors = [] + BorderSpacing.Right = 6 ParentColor = True object rmseries: TBarSeries BarBrush.Color = clYellow @@ -619,7 +620,7 @@ object audiofrm: Taudiofrm Left = 0 Height = 344 Top = 0 - Width = 711 + Width = 705 AllowPanning = False AllowZoom = False AxisList = < @@ -644,6 +645,7 @@ object audiofrm: Taudiofrm end item Grid.Visible = False + Intervals.Count = 9 Intervals.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps] Alignment = calBottom Arrow.Visible = True @@ -665,7 +667,6 @@ object audiofrm: Taudiofrm Foot.Brush.Color = clBtnFace Foot.Font.Color = clBlue Frame.Visible = False - GUIConnector = guiconnector Title.Brush.Color = clBtnFace Title.Font.Color = clBlue Title.Text.Strings = ( @@ -674,6 +675,7 @@ object audiofrm: Taudiofrm OnBeforeCustomDrawBackWall = spectrumscreenBeforeCustomDrawBackWall Align = alClient Anchors = [] + BorderSpacing.Right = 6 ParentColor = True end end @@ -682,7 +684,7 @@ object audiofrm: Taudiofrm Left = 0 Height = 344 Top = 0 - Width = 711 + Width = 705 AllowPanning = False AllowZoom = False AxisList = < @@ -707,6 +709,7 @@ object audiofrm: Taudiofrm end item Grid.Visible = False + Intervals.Count = 9 Intervals.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps] Alignment = calBottom Arrow.Visible = True @@ -735,6 +738,7 @@ object audiofrm: Taudiofrm ) Align = alClient Anchors = [] + BorderSpacing.Right = 6 ParentColor = True object spectrumseries: TBarSeries BarBrush.Color = clRed @@ -1127,8 +1131,4 @@ object audiofrm: Taudiofrm Left = 624 Top = 80 end - object guiconnector: TChartGUIConnectorBGRA - Left = 576 - Top = 144 - end end diff --git a/src/mainfrm.pas b/src/mainfrm.pas index fb45f2a..517451d 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -29,14 +29,13 @@ interface classes, sysutils, forms, controls, graphics, dialogs, buttons, stdctrls, extctrls, comctrls, tagraph, taseries, tasources, bufstream, soundwav, bcradialprogressbar, bclistbox, bcbutton, process, inifiles, bgrabitmap, - bgrabitmaptypes, bctypes, tadrawutils, taguiconnectorbgra; + bgrabitmaptypes, bctypes, tadrawutils; type { taudiofrm } taudiofrm = class(tform) blocksbtn: tbcbutton; - guiconnector: TChartGUIConnectorBGRA; freq: tlistchartsource; spectrumchart: tchart; page3: tpage; @@ -128,7 +127,7 @@ implementation {$R *.lfm} uses - tadrawerbgra, math; + math; { taudiofrm } From 42e8adf8811fe4386fe921084b0ea0b4c79fd0d4 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 1 Dec 2024 12:03:30 +0100 Subject: [PATCH 20/21] Fixing rsm and peak values --- src/soundwav.pas | 63 ++++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/src/soundwav.pas b/src/soundwav.pas index 58c9448..0cbf946 100644 --- a/src/soundwav.pas +++ b/src/soundwav.pas @@ -158,7 +158,6 @@ ttrackanalyzer = class(tthread) ffmt: tfmtchunk; ffmtext: tfmtchunkext; fdatachunk: tdatachunk; - fdata: array of ttrackchannel; //--- ftrack: ttrack; fstatus: longint; @@ -180,7 +179,6 @@ ttrackanalyzer = class(tthread) function getrms(channel: word): double; function getpeak(channel: word): double; function getdr(channel: word): double; - procedure clear; public constructor create(atrack: ttrack; astream: tstream; aspectrumon: boolean); destructor destroy; override; @@ -324,9 +322,7 @@ destructor ttrack.destroy; i: longint; begin for i := low(fchannels) to high(fchannels) do - begin fchannels[i].destroy; - end; fchannels := nil; inherited destroy; end; @@ -359,10 +355,8 @@ function ttrack.getchannelcount: longint; constructor ttrackanalyzer.create(atrack: ttrack; astream: tstream; aspectrumon: boolean); begin - fdata := nil; ftrack := atrack; fstream := astream; - clear; fspectrumon := aspectrumon; freeonterminate := true; @@ -371,22 +365,9 @@ constructor ttrackanalyzer.create(atrack: ttrack; astream: tstream; aspectrumon: destructor ttrackanalyzer.destroy; begin - clear; inherited destroy; end; -procedure ttrackanalyzer.clear; -var - i: longint; -begin - for i := low(fdata) to high(fdata) do - begin - fdata[i].destroy; - end; - fdata := nil; - fstatus := 0; -end; - procedure ttrackanalyzer.getspectrum(achannel: plongint; count: longint; aspectrum: pfloat); var i: longint; @@ -459,29 +440,31 @@ function ttrackanalyzer.getdr(channel: word): double; var i, n: longint; peak2nd: double; + chn: ttrackchannel; begin result := 0; - for i := 0 to fdata[channel].count -1 do + chn := ttrackchannel.create; + for i := 0 to ftrack.channels[channel].count -1 do begin - ftrack.channels[channel].add( - fdata[channel].frms2[i], - fdata[channel].fpeak[i]); + chn.add(ftrack.channels[channel].frms2[i], + ftrack.channels[channel].fpeak[i]); end; - fdata[channel].frms2.sort(@compare); - fdata[channel].fpeak.sort(@compare); + chn.frms2.sort(@compare); + chn.fpeak.sort(@compare); - n := trunc(0.2 * fdata[channel].count); + n := trunc(0.2 * chn.count); if n > 1 then begin - peak2nd := fdata[channel].fpeak[1]; + peak2nd := chn.fpeak[1]; result := 0; for i := 0 to n -1 do begin - result := result + fdata[channel].frms2[i]; + result := result + chn.frms2[i]; end; result := db(peak2nd/sqrt(result/n)); end; + chn.destroy; end; function ttrackanalyzer.getrms(channel: word): double; @@ -528,13 +511,13 @@ procedure ttrackanalyzer.execute; begin dr := getdr(i); ftrack.fdr := ftrack.fdr + dr; - ftrack.frms := ftrack.frms + getrms (i); - ftrack.fpeak := ftrack.fpeak + getpeak(i); + ftrack.frms := ftrack.frms + getrms (i)*fnorm; + ftrack.fpeak := ftrack.fpeak + getpeak(i)*fnorm; {$ifopt D+} writeln; writeln('track.DR [', i,'] ', dr :2:1); - writeln('track.Peak[', i,'] ', db(getpeak(i)):2:2); - writeln('track.Rms [', i,'] ', db(getrms (i)):2:2); + writeln('track.Peak[', i,'] ', db(getpeak(i)*fnorm):2:2); + writeln('track.Rms [', i,'] ', db(getrms (i)*fnorm):2:2); {$endif} end; ftrack.fdr := ftrack.fdr /ffmt.channels; @@ -589,11 +572,6 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); ftrack.fduration := format('%3.2d:%2.2d', [secondcount div (60), secondcount mod (60)]); end; // read samplecount - if status <> 0 then exit; - setlength(fdata, ffmt.channels); - for i := low(fdata) to high(fdata) do - fdata[i] := ttrackchannel.create; - samplecount := fdatachunk.subck2size div ffmt.blockalign; blocksize := trunc(132480 / 44100 * ffmt.samplespersec); blockcount := 0; @@ -604,7 +582,7 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); if blocksize = 0 then fstatus := -3; if status <> 0 then exit; // allocate track samples - setlength(ftrack.fchannels, ffmt.channels); + ftrack.channelcount := ffmt.channels; // allocate track samples spectrum if fspectrumon then for i := 0 to length(ftrack.fchannels) -1 do @@ -630,8 +608,9 @@ procedure ttrackanalyzer.readfromstream(astream:tstream); // calculate block rms and peak and db for j := 0 to ffmt.channels -1 do begin - fdata[j].add(getrms2(samples[j], i * blocksize, blocksize), - getpeak(samples[j], i * blocksize, blocksize)); + ftrack.fchannels[j].add( + getrms2(samples[j], i * blocksize, blocksize), + getpeak(samples[j], i * blocksize, blocksize)); ftrack.fmaxamp := max(ftrack.fmaxamp, getmaxamp(samples[j], i * blocksize, blocksize)); end; @@ -868,8 +847,8 @@ procedure ttracklist.savetofile(const filename: string); track := gettrack(i); s.add(format('DR%2.0f %7.2f dB %7.2f dB %4.0d %4.0d %7.0d %-s %s', [ track.dr, - db(track.peak), - db(track.rms), + db(track.peak)-db(1 shl (track.fbitspersample -1)), + db(track.rms )-db(1 shl (track.fbitspersample -1)), track.bitspersample, track.channelcount, track.samplerate, From 953804a7dc30e15f402e54e51322f0ec73e585c1 Mon Sep 17 00:00:00 2001 From: Melchiorre Caruso Date: Sun, 1 Dec 2024 18:48:11 +0100 Subject: [PATCH 21/21] Adding play button... Adding play button Spectrum drawer use max values --- audiometer.lpi | 3 + images/play-gray.png | Bin 0 -> 1294 bytes images/play-white.png | Bin 0 -> 841 bytes images/play.svg | 8 ++ images/stop-gray.png | Bin 0 -> 1160 bytes images/stop-white.png | Bin 0 -> 781 bytes images/stop.svg | 54 ++++++++++ src/mainfrm.lfm | 229 ++++++++++++++++++++++++++++-------------- src/mainfrm.pas | 91 +++++++++++++++-- 9 files changed, 305 insertions(+), 80 deletions(-) create mode 100644 images/play-gray.png create mode 100644 images/play-white.png create mode 100644 images/play.svg create mode 100644 images/stop-gray.png create mode 100644 images/stop-white.png create mode 100644 images/stop.svg diff --git a/audiometer.lpi b/audiometer.lpi index 096f0e6..cefd61a 100644 --- a/audiometer.lpi +++ b/audiometer.lpi @@ -76,6 +76,9 @@ + + + diff --git a/images/play-gray.png b/images/play-gray.png new file mode 100644 index 0000000000000000000000000000000000000000..663056c2e2f017b58c752b26a49e12ae15ff3b2b GIT binary patch literal 1294 zcmV+p1@ZccP){xk`m-ciD*DUNt)ooj3oh5aDgUWxL`$a0TwbWS~`^X zUjITUI2RlR5`oxGk_+g)OYg=FvhGDk#Agbl7j$t zDW%5q5$6!HR76e!*yK!)r_<>McPC~A7-OnLqvy?S+z zh_pHhT}~#GuLox$nDVR(4>N+c5bkI4BDiA1In zi9{`cS243I&nFUzxaa%+*5HM+_xxCaShXY%1Nc@cwYgXU7ICqs-tKslN3t=nk{l^? z&jX0aDw5~4)}I<=suN?QXA4UpV6eoD0AL${>&6&ujHxQ*>~fG?>xsw; z$6nH76F@|&MMR0nkH(lC`FwrfzvZl3;Q?6a7=JBCfP*C>@}buHyfJ3YJU$b_50W*W zYwSD^utfgP0KBV|`l%Ra%Ml2_<^j0i7}pzPmJ}0+ zPvfR~IhrM?^cf^LcAUC;BrpoloiSrRjvj^DYZSFPG9Wp?fs)fV%XR6dl6Y+r%q_C-=T;EJ>Yc_2@2AeB)68^ zh~yv-**gpQvY-TqA9Cg-g0&wL2AjizvdM_Xk#$8=qV{kj7iHV7h z`uh3}0A90Bd$rcplarHs1_lPwi3)i?7}qw&blIOCv)7k<#`L1y=r?&?sg6w0F3*- z-_+UJIqLf7{4j~dVvEAz@R^`{nX`Z#5s^U=8FvcHV$buI*+=G;9J(%&e9O%|uK)mO zX=zztyVody%6wVp!F>QnNE)To{ag+OKTeG?&j4rx@OBB`h<5;-%4V~NgVR1A3JP$9 zJO-c%KqG*4wvn0rJSRCOB3DJ^LMoLy+uz@Rx0t~H0)i_ysL6dW$p8QV07*qoM6N<$ Ef>Zxj$^ZZW literal 0 HcmV?d00001 diff --git a/images/play-white.png b/images/play-white.png new file mode 100644 index 0000000000000000000000000000000000000000..c11b7e18eb3a6c9b8b870af617611ab170eafc95 GIT binary patch literal 841 zcmV-P1GfB$P)zuXDTHmgcL?AW;y})6h z184)b+TM5IJun4410DhML0To18%>f1BrPf&i<0h2DwNZ|+Gd@kK1mB}Fzn-(q$`qY z((F{FgrET22lo3P4443hfgmkU85!T7MGT!3MJo9B?u2=7cNU z=Lz1bz_34Vd%7?C4m5e$K5X9qD+j72J#q9eOWNf+FyJLzTFj42+LqCYHc2JN&YjqS z2Cv9mT}8=|q;HbWN!pabiR+G?1xfWtI_)g3l=;<$9N9OLdNTYOD~^s+k=6DX6Truy z8V-A`AU&|Y5P~8w={$Ew)>vYU1S)dT3%mv{0JQ<~u8oce$a@}UK^<@&cn6#*BRlKN z^AXVCEPTr7K#UgP9xw*9r^tME=363f({Cb`AdP7JVIu;5I?v4&8vOvA1-gM(DKbsY z{6_>#I}5pthL?bgz;0lujBKkjpN@=1#puak{2{Q{{(@3~ylZ1BG7cA`%MvLBZ;gN) z17-rx$N zMkYeoaZb_=FX3d2c%Ltyb}?zRi^-vcy$NfnMW?YeTZQWZ4kny=0h|QB{B1Tf(K*jF4FFGy=JnJL0@0aK4=Wv=Ahr9yn?guG5H&e?6bH(*77&4AJ@ + + + + \ No newline at end of file diff --git a/images/stop-gray.png b/images/stop-gray.png new file mode 100644 index 0000000000000000000000000000000000000000..eb6c85c65e97d384d1c4c2c0eedae97c3d238052 GIT binary patch literal 1160 zcmV;31b6$1P)_gKx#OMCCDEo@lXvY2?P%&LV~86m}t_22TueKkd`bl1vh2B zDQ%>JBo;4F&K!E+q-dk|q!3~nLShpSgr-&7XtKmVbW_;w_dGBk8P?tHE_B~%W_~lj z_vZWN&l^fYJ?QQ2eKM2DyiIZs$!!2O19-}Z|B^IC@&Oj;o-Y= z3vkYD1#n5yOJTbtk(>bVjiec4%$yIcm-H-vHzn;L$+DDJipAo)gM)*AEGfV_w_Va0 zfTx4z1(Ih03|nh|t2Jxq+y+TK01g4j1YhG<0G-y_pQ{RR&TWU*Gc&1f#%fOFDx4Uu7P^GpRU^-y*pZexDm-&Q?ll zjM*jWoq7ZqW2%LQC1puc(khajhUA_kN^)YUef~JG*3OW;p427nHU45r1dKI{yb=e= zZAQ|TAl}en^C*bpcseOd+F}5#34*^jTL6IBq(HLmq19rHDZfOP9pxMbz&}BKeX|9` zaonDiCH-vx+zR3wn=QZ?vniTw)@ zp91hDfI9$I`xB}Mz(?hS<2b%nIP-+t=RSz8k=6zEe*WMXhslbBE5IJ6EidyCxD#mbf90 zE4Odo?oTQYnbKS?x2~n7z!2k0L;mk=+1K4S;{j;I~Kk!8W zmqQWW$$a5$dCkS?w^|R$gTaCWfSEXsJNx_lC&T^~A11k6Ze2Q^z69Xa@)*epNn?^` zf{SInF=mrLWL~MD>j#qWg)^@y006qXyH|Mkng!5OYt}Vz55Q@X&RTn~QimlUr_Q>2f%i3WF9=vNlr<+E@`|_C|o{u>ePHa aiT?w5$?O_$|0<^d0000WdP)33L>Jd5QZt#O3eQuT=e0T z1wjce2Js)T9Qwdkl@4hOpEmD>)7!l>*Rh!%IB-7S=e_rR&pFR&kwiuu04@RNfB~Qn zIAmkrfcL->@EUjutYxw)saENdG%2Z~?5IeZmQ<)^zuMqFNh6Zhw?XXjOVT|_ZD~9$ zDIq8TkAai^hXEyE7AON>tiKyL3S0n&fEKrI9=HswW(mQdq*eDNKP8Px>d6qY9!d9Y zO^C9j{#qd@B#_Ta%4Hz-B`2xqY^_M@Omkqbq(!%2N>XbbX7lHjvNykG6CSAhS6Z2Z7Ptr!f^C;ei9k!kJ+r}6J zZYONbJHVMtryDE)qY3@vz)h=HfFnq{7I?K)Wvk67J7%ivjICha)yU5F7$x9i#;>dg zWneLApO5TfiBW7Ic@oD!1mvBEjmAn)&qqK<(DA7W4tx&k2O~G=FOf#z4~_`<5wyFT z;6PVU{~iI$K}W6$4)g}~<;Y}IjNv9Y5OORQ&jF``_N!Ll39t^dTY_8% zZY3;U*alJB+#5j>R)FX65jp)IE~BaNHY}Kcj8WX75J|d@=K}j1lJZ`Vc;(VaL2}m% z5+v=G^u|q`vi^FBlO)-@G)aaD#BRwPx$$150g%Nn4C=*E7_G=4lb5%JK=_b(+O+d0#|^qe~nc6k~UMXo2nn1mT-I0 zc(TOum + + + + + diff --git a/src/mainfrm.lfm b/src/mainfrm.lfm index 61550e8..ec65090 100644 --- a/src/mainfrm.lfm +++ b/src/mainfrm.lfm @@ -38,13 +38,12 @@ object audiofrm: Taudiofrm object btnfile: TImage AnchorSideTop.Control = btnfolder AnchorSideRight.Control = btnfolder - AnchorSideRight.Side = asrBottom - Left = 625 + Left = 628 Height = 32 Top = 34 Width = 32 Anchors = [akTop, akRight] - BorderSpacing.Right = 45 + BorderSpacing.Right = 10 Picture.Data = { 1754506F727461626C654E6574776F726B47726170686963C102000089504E47 0D0A1A0A0000000D49484452000000200000001E08060000004D0A1C29000000 @@ -539,7 +538,7 @@ object audiofrm: Taudiofrm Height = 344 Top = 222 Width = 711 - PageIndex = 2 + PageIndex = 0 Anchors = [akTop, akLeft, akRight, akBottom] BorderSpacing.Left = 3 BorderSpacing.Top = 5 @@ -575,6 +574,7 @@ object audiofrm: Taudiofrm end item Grid.Visible = False + Intervals.Count = 10 Intervals.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps] Alignment = calBottom Arrow.Visible = True @@ -645,7 +645,7 @@ object audiofrm: Taudiofrm end item Grid.Visible = False - Intervals.Count = 9 + Intervals.Count = 10 Intervals.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps] Alignment = calBottom Arrow.Visible = True @@ -709,7 +709,7 @@ object audiofrm: Taudiofrm end item Grid.Visible = False - Intervals.Count = 9 + Intervals.Count = 10 Intervals.Options = [aipUseCount, aipUseMinLength, aipUseNiceSteps] Alignment = calBottom Arrow.Visible = True @@ -1043,92 +1043,175 @@ object audiofrm: Taudiofrm TextApplyGlobalOpacity = False MemoryUsage = bmuHigh end + object btnplay: TImage + AnchorSideTop.Control = btnfile + AnchorSideRight.Control = btnfile + AnchorSideBottom.Control = btnfile + AnchorSideBottom.Side = asrBottom + Left = 584 + Height = 32 + Top = 34 + Width = 32 + Anchors = [akTop, akRight, akBottom] + BorderSpacing.Right = 12 + ImageIndex = 5 + Images = buttons + OnClick = btnplayClick + OnMouseDown = btnplayMouseDown + OnMouseLeave = btnplayMouseLeave + OnMouseMove = btnplayMouseMove + OnMouseUp = btnplayMouseUp + end object filedialog: TOpenDialog - Left = 512 - Top = 16 + Left = 544 + Top = 120 end object rms: TListChartSource SortDir = sdDescending - Left = 576 - Top = 16 + Left = 544 + Top = 168 end object peak: TListChartSource - Left = 576 - Top = 80 + Left = 592 + Top = 168 end object dirdialog: TSelectDirectoryDialog - Left = 512 - Top = 80 + Left = 592 + Top = 120 end object buttons: TImageList Height = 32 Width = 32 - Left = 448 - Top = 16 + Left = 496 + Top = 120 Bitmap = { - 4C7A0400000020000000200000006D0600000000000078DAED9B5B48155D14C7 - 354133CB286F5959919262A979CD308D3EB5BC80082A5A5ACAA79F9F17D4322B - EFBE758F4AC45E7CEEC127412882507B0A41A112F4A944C12CA142BCA6D9FAFA - 6F98381E67C63333AD937D9C0D7FC8E69CF9CD9ABDF7DA6BEDB58F9D9D1DD96D - 20F9F9F9D1DBB76F494BFBFEFD3BB5B6B692BDBDBD61FEB56BD7686464841212 - 122CD6F0F0B0788EEBD7AF1BE6373535515F5F9FA6EFF4F6F6D2F3E7CF697171 - 91AAABAB7F0BBFB9B999D2D3D3697979990A0B0BADCAEFE9E9A18989091A1818 - A0999919F10CE1E1E156E39F3871428C1BA8B6B696666767293737D730FFC891 - 239AC6A1AFAFAFF8DEF8F8B861FEA64D9B687A7A5AD33C1C1A1AD2C5F7F1F111 - FDB56BD7AE55F61F3A744893FDFBF6EDD3CCC79C951AFA4F4FFF9B4B0B1F6305 - FEEEC2850B14101060753E7CE7EDDBB765FBFFC99327E2D92C554747872EFECD - 9B3765C7FFDDBB77A9B3B3D36261EE99F28B8A8A2829294937DFE8FBBF75EB16 - 7DF8F041F5B3685C7CF42BECD36B3FFC5A666626EDD8B143131FF78B8A8A32C4 - C7F8FBF2E50B95949490BFBFBF58D7241F6BA95EBC7821EEAF74FDE4C993AAF6 - 7B7979D1EEDDBBE9E3C78FC4D13E7DFAB4EEF87BF8F02171B5C78F1FAB8E3FF8 - E3F9F9791636D668F83B35FE83070FD86C7FF4E8D1CFF987796ACA7FF6EC19AB - EDB8EF9E3D7B14E77F767636DDBB778FCD76539EB9FD616161E4EDED4D737373 - 2C6CCCE99D3B772AFA3FE8CE9D3B6CB6D7D7D7ABFA5F77777711437234F8916D - DBB6A9F2D1175CADBCBC5C76FDC13CE3B61DF181A3A3E31AFEE7CF9F851FBC7F - FF3E3D7DFA94CDF6BCBC3CD9F5273F3F5F3C0357434E76E3C68D0D95636F64C1 - 37545656524B4B8BC542EE999C9CFC4BF8313131629E1C3C78D062E1F3788EF8 - F878C3FCB8B8382A2828D0F41D8C61E40F8D8D8D74FCF8F1DFC2472C25E52FA1 - A1A156E7233E2C2E2EA6BABA3AF10C58C3ACC547DE8971032166C61A13141464 - 98EFE9E9A9691C4A71FAA54B970CF3B197867C4ACB3C2C2D2DD5C577757515FD - B575EBD655F6BBB9B969B27FFBF6ED9AF998B3D2F3A3FFF4F4BFB9B4F03156E0 - EF424242C4FA6B6D3E7C676262A26CFF2387C4B359AAB4B4345D7CECDDC88DFF - D3A74F53565696C5C2DC33E52396C57EB25EBED1F78FFBD6D4D4A87E16E38E8B - 8F7E857D7AED875F0B0C0CA4CD9B376BE2E37EC86F8CF031FE90A3474444083F - 80754DF2B1960AF7C1FD95AEEFDFBF5FD57E17171711AFA30FB5F8414B75F5EA - D575C71FF6AF38D850464686EAF8833F6E68686061638D86BF53E373DA9E9A9A - AA38FF902B70DA8EFB4A79A01CFFF0E1C3C2F771D96ECA33FF5B5A87B12E71B0 - 31A79D9D9D15F910FC0697EDB1B1B1AAFE77CB962D2286E460C38F98E7C0E67C - FC9BCBF6C8C848D9F547DA23E7B41DF1818383836CBD157EF0CC993322DEE0B2 - 3D38385876FD41DC8567E0E22227FB1579A14D36FD1FF5EAD52BD97DB36FDFBE - 198E832DD1D2D212BD7CF952ECC19AEADDBB77E2D9B8F9A883C89D1B410C8F86 - 1C8693FFF5EB5759BE878787B8F6FAF56B4D355853B5B7B7D3DEBD7B55F9D81F - 553A37837D532DF567732D2C2C505757972EFB7F85BABBBBE9CD9B377F3C1FB9 - 1CEAC05A727168707090C6C6C614AF634DC6FC53E3A30EF7FEFD7B96BDF9C9C9 - 49C5F927A9ADAD8DAD3680D8408D8F589CAB06393535257210F859253ECED471 - B58A8A0AC150E273DA3E3A3A4A4E4E4E82B3B2B222CBE7ACBB9F3F7FFE27478E - CF597BC6192D9CAD303D7F61CE472D8EAB497B844A7C9CB9E0AA3DF7F7F7AF39 - 236A7E6E93B3EE7EEAD429D9FAABC4E7ACBFE22C97D2F933BC17C43C38F7C0D1 - 30C6956A425555556C36A361FD292B2BB3C5DA36D964934D1B4C25FF96504B73 - CB1A353735D3D190A3ECFCA6C6262AFCBB9012E21356A9AAB24A3C9B35F8F17F - ADDD1F8C8E8E16EF41EF39064BD5D8D028CBC75E30AEE11D646566E9526A4AAA - A8EDEBE14BB5F9CA8A4ADD6AA86FA09CEC1CDD7CA33A9B73964A4B4AFF783EF2 - 44D481B5AAF89F62BA587551F13A627DA5F12F09B5A8CBD597657D8451E1BEEB - F153925358D8505C6C9C2A5FD4207F8C610EF6959A2BA216053FABC44F4E4A66 - B31DBF0F10F57F053EA7EDF0ED522D4A892F6ACF4CB69BD6A2E4F89CB6C31F98 - E6C0387F60CE472D8ECB76FC96C3FCFC87291F672EEAEBEA59D84585456BEB9F - 3FFEDF942FEAEE4CB61F387040952FEAAFB5752CECDC73F2BFC7C2BBC67B41CC - 93979BC7C2C618C79E961CFFD8B1636C3643587F2223226DB1B64D36C9E83F10 - 6FB457 + 4C7A080000002000000020000000070C00000000000078DAED9C576C15471B86 + 2140E8A17708BD0842EFA2858E6902D14CE898DE0C846A6C40F4DE842809575C + 70C115121225A10A0109451022056EE8A2250284E875FE3C230D1AAF77CFD936 + 36F9B5237D927DCE9E7DA7BEF3CDBCF34DB66CD944B62FC8AA55AB26AE5FBF2E + BCA44F9F3E892D5BB688ECD9B307C69F3B77AEB87AF5AAE8D4A9936BFBEBAFBF + 643E56AC5811183F2D2D4D9C3871C2D36F8E1F3F2E8E1C3922DEBC792366CE9C + 9925F80B172E147DFAF411EFDFBF17494949998A7FECD83171EFDE3D71E1C205 + F1FCF9739987C68D1B671A7EEBD6AD65BFC1E6CD9B275EBC7821860C191218FF + BBEFBEF3D40FAB56AD2A7F77E7CE9DC0F85F7DF59578F6EC99A771F8E79F7FFA + C2AF50A1826CAFD2A54BA72B7F8D1A353C95FFDB6FBFF58CCF985589F6F3D3FE + 56F3824F5F81EF860F1F2E6AD5AA95E9F870E79A356B6CDBFFC08103326F6E6D + D7AE5DBEF057AD5A65DBFFD7AD5B27F6EEDDEBDA187B3AFE98316344B76EDD7C + E307ADFFD5AB578B870F1FC67C96640A9F76A57C7ECB0FAFF5EFDF5F142952C4 + 133EEF6BD6AC59207CFADFD3A74FC584091344CD9A35E5BCA638D6AD9D3C7952 + BEDFE9FB76EDDAC52C7FA952A544D9B265C5A3478F8489F4F8F1E3B8FD6FF3E6 + CDC254DAB3674FCCFE071FBF7AF5CA083673347C170B7FD3A64DC6CABE7DFBF6 + CFE38F71AAE31F3E7CD868D9796FB972E51CC7FFA04183C4FAF5EB8D955DC7B3 + 96BF51A346A24C9932E2E5CB9746B019D3458B1675E43F6CEDDAB5C6CA9E9292 + 12937F8B172F2E7D4813091E2958B0604C7CDAC2549A3C79B2EDFCC338335D76 + FC83AFBFFE3A03FE93274F240F6EDCB8511C3C78D058D9870E1D6A3BFF8C1831 + 42E6C154624DB672E5CA2F6A8DFD251BDC306DDA34B168D122D7C6DA33212121 + 14FC56AD5AC97152A54A15D7C6F3E4A363C78E81F1DBB66D2B468E1CE9E937F4 + 61D60FA9A9A9A265CB9659828F2FA5D62F0D1B36CC747CFCC371E3C689F9F3E7 + CB3C308765163EEB4EFA0D86CFCC1C53B76EDDC0F8254B96F4D40F959F3E63C6 + 8CC0F8ECA5B19EF2320E274E9CE80BFF9B6FBE91ED55A0408174E52F56AC98A7 + F2172A54C8333E6356E59FF6F3D3FE56F3824F5F81EFEAD7AF2FE7DFCCC6873B + 3B77EE6CDBFEAC21C99B5BEBDDBBB72F7CF66EECFA7F972E5DC48001035C1B63 + 4FC7C797653FD92F7ED0FAE7BDB366CD8AF92CFDCE143EED4AF9FC961F5EAB5D + BBB6C893278F277CDEC7FA26083EFD8F357A93264D240F30AF298E756BBC87F7 + 3B7D5FB162C598E5CF9F3FBFF4D769432F3CE8D6E6CC9913B7FFB17F65021BEB + D7AF5FCCFE071F2F58B0C0083673347C170BDF64D97BF4E8E138FE582B982C3B + EF55EB403BFC3A75EA48EE3355761DCFFABF9A8799974C6033A6F3E6CDEB888F + C11BA6CADEA64D9B98FC9B2F5F3EE9439AC08647AC6B602B3E7F9B2A7BD3A64D + 6DE71FB5476EB2ECF8073972E4B0D55BE1C1AE5DBB4A7FC354D9EBD5AB673BFF + E077910753B8ACC9C258174616D9FFA35DBE7CD976DFECC3870F81FD6037F6EE + DD3B71F6EC59B907ABDB8D1B3764DE4CE3A383D89D1BC18727B1863189FFF6ED + 5B5BFC12254AC8EFFEF8E30F4F1AAC6EDBB66D13E5CB978F89CFFEA8D3B919F6 + 4DBDE8CF567BFDFAB5D8B76F9FAFF28761FBF7EF1757AE5CF9CFE3B3964307F6 + B216C72E5EBC286EDFBEEDF83D7332E32F163E3ADCFDFBF78DECCD3F78F0C071 + FC29DBBA75AB316D00DF20163EBEB8290DF2EFBFFF966B1078D6099F3375A6D2 + D4A953258613BEC9B2DFBC7953E4CE9D5BE27CFCF8D116DFA4EE3E6CD8B0CF38 + 76F826B567CE6871B6423F7F61C5478B3395D41EA1133E672E4C69CFBFFFFE7B + 8633A2D6739B2675F7F6EDDBDBEAAF0ADFA4FECA592EA7F367D40B3E0FE71E4C + 24FAB89326949C9C6CACCC24E69F49932645BE7664914516D9176613C64F108B + 162ECA600BD3168A06F51B18C74F4B4D1349A39344A78E9DD259F2B46499B7CC + C0EFD821E3FE608B162D643DF83DC7E0D65217A4DAE2B317CC77D4C180FE037C + 598FEE3DA4B6EF075F69F3D3A64EF36D0B521688C44189BEF183DAE0C4C162E2 + 8489FF797CD689E8C05E6DDCD871627AF274C7EFF1F59DFABF32B4A81F67FE68 + CB11418DF7C6C3EF9ED0DD0836D6B64DDB98F85283FCB70F9BC09E3D6BB6D4A2 + E05927FC846E09C6CA4E7C80D4FF1DF04D961D6E575A9413BED49E0D955DD7A2 + ECF04D961D3ED0D7C09C3FB0E2A3C5992A3BB11CD6F31F3A3E672E52E6A718C1 + 1E933426A3FEF9EFE73ABED4DD0D95BD52A54A31F1A5FE3A6FBE11EC213FD8C7 + 6351D7D40B3ECFD021438D60D3C7D9D3B2C36FDEBCB9B13263CC3F4D9B348D7C + EDC8220B60F88083070F163FFFFCB33877EE9CD40FD06E30FE660FF1A79F7E12 + 89898919625C8218EB1F62B5BCEC09F32CF98C77F63396714669C99225727FD4 + 6FA26ED857555A83973B10D0EDECEE39387FFEBC58BA74A9183B76ACE8D9B3A7 + 34CEBDF319DFF18C359D3E7D5AFADA6EB01B34689021DE111D9558CE785A2EC6 + 33C46FF11B3D11934A3C7BBC725BB10F1D3AF439AEDBEB997CEB7E367BC14EF5 + C019D74B972EA57B7EE7CE9D2267CE9CBEFB103E26EDAFA73367CED8F607FA9A + 9E544C561866D5B2ACF1488C31BD9F53E77667B5FC1A75F8CB2FBF7C7E3F9AB3 + BAAB00637CEB7DCDA9BDFBF6ED2BCF25F8C903BE17670D5482AB54EC81CE2D7A + 2CA4D576EFDE2DE317392F6617CB16CF88675789FAC6CF8753F5F11D6B8C81AF + D2B56BD7A49FEE754CE8FC3070E040C9932AC11FB17EAFE3ABC419033BBFD6C9 + 3813A08F2FE61295962D5BE6195FC5D32E5EBCD8D53975FDBE87DF7EFB4DFCF3 + CF3F9FFF8747FDE0AB74F7EE5D198715EB1DE3C78F4F170FCA5850499D49F68B + AFD2D1A3471DB9B657AF5EE96213757CE692B0F039C36CF70EF45F1D3FCCFA67 + 8EF15AFFF82D59D5FF3877060F65D5F8DBB16387F4D7B28A7F38030407EAFA2F + BE8329FEDDB0614306FEE5739D03336BFE81FB74BF075F5125FC96207E87DDFC + CBFD50FAB8AB5CB972863B3B32CBFF58BE7C79866772E5CA25EFEBB0FA5F7C1E + A6FF852FECE48FE31BC2217AFAF5D75F659C909FF6D6EB9C74EBD62D19D319EB + 77F0B6350FB417BE831B3F986738C3A2F735E5FB3A71B25D3D504F76893BB668 + 3F7894B904E36F784DE7163D9D3A752A6EB9AD461BE1A7EAF393D7C46FC9AB1F + AE50465C291CED651D089FD177AD632C88C155F86B70367E8BF21D30FEE633FC + 6838953DD4B0D7FFD4217D93F666ED397BF66C19C385F137772DF11DCF04A96F + BB72E317798947E259F2A2DF33E2873339AF15240E8ABA219EC7EB5A8A7C134B + 6DF73EEA9E7C712E9CFBD130EE4EE333BEE319EBEF468F1EEDBA4FB04F6B8D77 + 245E8D98AC785AAE5A4FF1AC35768EF3ECF13880725BB189C55371DD5E8CDFF0 + 5BFD5DD3A74F77AC07DA1B1ED39FC71FD6CF4A7A3574166B3C177723DAF507DA + 4F7F2EDEBD655ECC1A47698D47B2C6FB516F61DCA5A98C3AE4CCAB1E13A5DF29 + C6F8D6FB9A537B73BF041A899F3C142E5C58E22A1CF841CD337AD9ADB18856FF + 0FDF93D8613FFB237A4C27988A53F5F11D6B8C81AF9E9D32654ABA3D14B76342 + E707FC01EA41FD0F7FC4FABD8EAF8CFD0BEAD66D1E58E3E9E30B4CF57F870E1D + 3CE3AB3EF3FDF7DFBBF299F5FB1E98B398BBD4FFF1EE0C75C2D7398E78C258EF + 00438F07D5FB64F5EAD503E12B630DECC4B5CC17FA38D4F1F92E2C7CA77512FA + AF8E9FD5F54F1FC8AAFEC75C90D5E32F2BF987334070A01E73ACDF451236FFCA + 730D16FE557B62993DFFE87B6DF83D7ABD30FF06F13BECE65FC6A43EEEAC7DC6 + EAA798F43FECC6197924DED3B4FF852FECD477F00DE110FD79FC163FFE27F5AB + D7793CFF5319BC6DCD03ED85EFE0261F3CC3B37A5F53D86EF7AEC823F564C775 + F007ED078FEAEB0F784DE716DD468D1AE5794D4A1BF14E6B39BCC6DE93D7205A + 167E2AFCE0651D089FD177BDF0B29BF537FE1AEF65CE52BE03A6E631FC683835 + C89E59A4FF47FA7FA4FF47FAFF97A8FF07B1A0FA3F9CCE5D026E8C67C3D6FF79 + AFDBE474E74110FD3F0CFC20FA7F58F87EF5FFB0F0FDEAFF61E1FBD5FFC3C2F7 + ABFF9BAA7FB7FABF89FEE745FF3731FEBCE8FF26F8C78BFE1F06FF06D5FFC39C + 7F22FD3FD2FF23FD3FD2FF23FD3FD2FF23FD3FD2FF23FD3FBEFE12C482EAFFDC + 2D8FAFECC6D43DF461EAFFBCD72DEFF06CD8FA7F18F841F4FFB0F0FDEAFF61E1 + FBD5FFC3C2F7ABFF8785EF57FF3755FF6EF57F13FDCF8BFE6F6AFC6525FF78D1 + FFC3E0DFA0FA7F98F34FA4FF47FA7FA4FF47FA7F56EBFFFF03CA6E8DB4 } end object report: TImageList Height = 146 Width = 446 - Left = 448 - Top = 80 + Left = 496 + Top = 168 end object freq: TListChartSource - Left = 624 - Top = 80 + Left = 640 + Top = 168 + end + object playsound: Tplaysound + About.Description.Strings = ( + 'Plays WAVE sounds in Windows or Linux'#10'Public methods: Execute and StopSound' + ) + About.Title = 'About PlaySound' + About.Height = 400 + About.Width = 400 + About.Font.Color = clNavy + About.Font.Height = -13 + About.BackGroundColor = clCream + About.Version = '0.0.8' + About.Authorname = 'Gordon Bamber' + About.Organisation = 'Public Domain' + About.AuthorEmail = 'minesadorada@charcodelvalle.com' + About.ComponentName = 'PlaySound' + About.LicenseType = abModifiedGPL + PlayCommand = 'aplay -q' + Left = 640 + Top = 120 end end diff --git a/src/mainfrm.pas b/src/mainfrm.pas index 517451d..e9fbc4b 100644 --- a/src/mainfrm.pas +++ b/src/mainfrm.pas @@ -26,10 +26,10 @@ interface uses - classes, sysutils, forms, controls, graphics, dialogs, buttons, stdctrls, - extctrls, comctrls, tagraph, taseries, tasources, bufstream, soundwav, - bcradialprogressbar, bclistbox, bcbutton, process, inifiles, bgrabitmap, - bgrabitmaptypes, bctypes, tadrawutils; + classes, sysutils, uplaysound, forms, controls, graphics, dialogs, buttons, + stdctrls, extctrls, comctrls, tagraph, taseries, tasources, bufstream, + soundwav, bcradialprogressbar, bclistbox, bcbutton, process, inifiles, + bgrabitmap, bgrabitmaptypes, bctypes, tadrawutils; type { taudiofrm } @@ -37,6 +37,8 @@ interface taudiofrm = class(tform) blocksbtn: tbcbutton; freq: tlistchartsource; + btnplay: TImage; + playsound: Tplaysound; spectrumchart: tchart; page3: tpage; spectranalisysbtn: tbcbutton; @@ -87,6 +89,10 @@ taudiofrm = class(tform) procedure btnfoldermouseleave(sender: tobject); procedure btnfoldermousemove(sender: tobject; shift: tshiftstate; x, y: integer); procedure btnfoldermouseup(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); + procedure btnplaymousedown(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); + procedure btnplaymouseleave(sender: tobject); + procedure btnplaymousemove(sender: tobject; shift: tshiftstate; x, y: integer); + procedure btnplaymouseup(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); procedure formclosequery(sender: tobject; var canclose: boolean); procedure formcreate(sender: tobject); procedure btnfileclick(sender: tobject); @@ -96,6 +102,7 @@ taudiofrm = class(tform) procedure btnfilemouseup(sender: tobject; button: tmousebutton; shift: tshiftstate; x, y: integer); procedure formdestroy(sender: tobject); procedure formresize(sender: tobject); + procedure btnplayclick(sender: tobject); procedure onstart; procedure onstop; procedure onprogress; @@ -127,7 +134,7 @@ implementation {$R *.lfm} uses - math; + math, fileutil; { taudiofrm } @@ -188,6 +195,31 @@ procedure taudiofrm.formresize(sender: tobject); end; end; +procedure taudiofrm.btnplayclick(sender: tobject); +begin + case btnplay.imageindex of + 6: begin + playsound.stopsound; + btnplay.imageindex := 5; + end; + 7: begin + playsound.stopsound; + btnplay.imageindex := 5; + end; + else begin + playsound.stopsound; + btnplay.imageindex := 5; + if fileexists(tempfile) then + begin + playsound.playstyle := psasync; + playsound.soundfile := tempfile; + playsound.execute; + btnplay.imageindex := 6; + end; + end; + end; +end; + procedure taudiofrm.formclosequery(sender: tobject; var canclose: boolean); begin trackkill := true; @@ -205,6 +237,7 @@ procedure taudiofrm.onstart; audio.caption := cutoff(audio.caption); end; + btnplay .enabled := false; btnfile .enabled := false; btnfolder .enabled := false; drvalue .visible := false; @@ -238,6 +271,7 @@ procedure taudiofrm.onstop; -3: audio.caption := 'file is too short!'; else audio.caption := 'unknown error!'; end; + btnplay.enabled := true; btnfile.enabled := true; btnfolder.enabled := true; progresspanel.visible := false; @@ -310,6 +344,7 @@ procedure taudiofrm.onstop; // drvalue .visible := true; + btnplay .enabled := true; btnfile .enabled := true; btnfolder .enabled := true; progresspanel.visible := false; @@ -592,10 +627,10 @@ function taudiofrm.drawspectrum(atrack: ttrack; awidth, aheight: longint): tbgra // skip DC component if index mod windowsize <> 0 then begin - amp := amp + db(atrack.maxamp*atrack.channels[k].spectrum[index]/maxamp)/db(1 shl atrack.bitspersample); + amp := max(amp, db(atrack.maxamp*atrack.channels[k].spectrum[index]/maxamp)/db(1 shl atrack.bitspersample)); end; end; - result.setpixel(i, j, getcolor(amp/atrack.channelcount)); + result.setpixel(i, j, getcolor(amp)); end; end; end; @@ -653,6 +688,8 @@ procedure taudiofrm.btnfileclick(sender: tobject); tracklist.clear; if filedialog.execute then begin + playsound.stopsound; + btnplay.imageindex := 5; if filesupported(extractfileext(filedialog.filename)) then begin tracklist.add(filedialog.filename); @@ -677,6 +714,8 @@ procedure taudiofrm.btnfolderclick(sender: tobject); tracklist.clear; if dirdialog.execute then begin + playsound.stopsound; + btnplay.imageindex := 5; path := includetrailingbackslash(dirdialog.filename); err := sysutils.findfirst(path + '*.*', faanyfile, sr); while err = 0 do @@ -818,6 +857,44 @@ procedure taudiofrm.btnfoldermouseup(sender: tobject; button: tmousebutton; btnfolder.onmousemove := @btnfoldermousemove; end; +procedure taudiofrm.btnplaymousedown(sender: tobject; button: tmousebutton; + shift: tshiftstate; x, y: integer); +begin + btnplay.onmousemove := nil; + case btnplay.imageindex of + 4: btnplay.imageindex := 5; + 5: btnplay.imageindex := 4; + 6: btnplay.imageindex := 7; + 7: btnplay.imageindex := 6; + end; +end; + +procedure taudiofrm.btnplaymouseleave(sender: tobject); +begin + case btnplay.imageindex of + 4: btnplay.imageindex := 5; + 6: btnplay.imageindex := 7; + end; +end; + +procedure taudiofrm.btnplaymousemove(sender: tobject; shift: tshiftstate; x, y: integer); +begin + case btnplay.imageindex of + 5: btnplay.imageindex := 4; + 7: btnplay.imageindex := 6; + end; +end; + +procedure taudiofrm.btnplaymouseup(sender: tobject; button: tmousebutton; + shift: tshiftstate; x, y: integer); +begin + btnplay.onmousemove := @btnplaymousemove; + case btnplay.imageindex of + 5: btnplay.imageindex := 4; + 7: btnplay.imageindex := 6; + end; +end; + procedure taudiofrm.btnloadicon(btn: timage; index: longint; x, y: longint); begin btn.center := false;