Home > Blockchain >  Logarithmic-linear value conversion
Logarithmic-linear value conversion

Time:10-19

The code below draws a logarithmic grid with DrawGrid(). It seems the vertical lines are ok. When I use the function SetPositionHzValue() the resulting position seems ok (it uses the same logic as the DrawGrid() and seems to match the grid). But how to convert this 0 - 1.0 normalized value, that uses the display width linearly, to an actual Hz value? Why is the function GetPositionsHzValue() wrong?

To complicate things, the display has a start frequency (20 Hz in this case) and an end frequency (44100 Hz in this case).

procedure TAudioBezierCurves.DrawGrid(Bitmap32: TBitmap32);
var
    GridPosition: Integer;
    GridPositionF: Double;
    i: Integer;
    Base: Double;
    LogOffsetValue: Double;
    LogMaxValue: Double;
begin
    GridPosition := 0;
    Base := 1;
    if GridFrequencyMin = 0 then begin
        LogOffsetValue := 0;
    end else begin
        LogOffsetValue := Log10(GridFrequencyMin);
    end;
    LogMaxValue := Log10(GridFrequencyMax) - LogOffsetValue;
    repeat
        for i := 2 to 10 do begin
            if Base * i < GridFrequencyMin then begin
                Continue;
            end;
            //* This gives the % value relative to the total scale
            GridPositionF := (Log10(Base  * i) - LogOffsetValue) / LogMaxValue; 
            GridPositionF := GridPositionF * Bitmap32.Width;
            GridPosition := Trunc(GridPositionF);
            Bitmap32.VertLineS(GridPosition, 0, Bitmap32.Height - 1, GridColor);
        end;
        Base := Base * 10;
    until GridPosition > Bitmap32.Width;
end;

procedure TAudioBezierCurve.SetPositionHzValue(AValue: Double);
var
    LogOffsetValue: Double;
    LogMaxValue: Double;
begin
    if AValue = 0 then begin
        Self.Position := 0;
    end else begin
        if Parent.GridFrequencyMin = 0 then begin
            LogOffsetValue := 0;
        end else begin
            LogOffsetValue := Log10(Parent.GridFrequencyMin);
        end;
        LogMaxValue := Log10(Parent.GridFrequencyMax) - LogOffsetValue;
        //* This gives the % value relative to the total scale
        AValue := (Log10(AValue) - LogOffsetValue) / LogMaxValue;
        Self.Position := AValue;
    end;
end;

function TAudioBezierCurve.GetPositionsHzValue: Double;
var
    AValue: Double;
begin
    AValue := Self.Position;
    AValue := Power(AValue, 2);
    Result := AValue * (Parent.GridFrequencyMax);
    Result := Result - (AValue * Parent.GridFrequencyMin)   Parent.GridFrequencyMin;
end;

EDIT: Ok, almost ok now. So it seems the correct function is:

AValue := Power(AValue, 10);

But still not perfect. Changing the display range to min. 0 to 44100, for simplicity, results that setting to the upper value 44100 is ok, the function GetPositionsHzValue() report 41100. But calling setting the position value to 20, GetPositionsHzValue() reports 0. Trying to decrement the position all is fine until 44085, but 44084 value is reported as 44085 and this difference increases with smaller values. Going from lower values, it's 0 until 39, 40 results 1.

CodePudding user response:

In function GetPositionsHzValue, line "AValue := Power(AValue, 2);" where does the value of "AValue" come from?

Maybe you should do something like you did in "SetPositionHzValue(AValue: Double);". AValue should be a parameter, not a local variable.

CodePudding user response:

Found the solution, it should be:

function TAudioBezierCurve.GetPositionsHzValue: Double;
var
    AValue: Double;
begin
    AValue := Self.Position;
    AValue := AValue * Log10(Parent.GridFrequencyMax)   (Log10(Parent.GridFrequencyMin) * (1 - AValue)); //* Results "min." at 0
    Result := Power(10, AValue);
end;
  • Related