You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

对数-线性值转换:归一化值转Hz值的函数错误排查

对数频率网格的归一化值与实际Hz值互转问题

当前通过DrawGrid()绘制的对数网格竖线显示正常,SetPositionHzValue()能正确将Hz值转为0-1的归一化位置(与网格匹配),但反向转换(把0-1归一化值转回实际Hz值)的GetPositionsHzValue()函数失效,需要解决这个反向转换的逻辑问题。已知显示频率范围是20Hz到44100Hz。

相关代码如下:

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;

编辑补充:已尝试将GetPositionsHzValue()中的AValue := Power(AValue, 2);改为AValue := Power(AValue, 10);,但问题仍存在:

  • 当显示范围设为0到44100Hz时,设置44100Hz返回正常,但GetPositionsHzValue()返回41100Hz;设置20Hz时返回0。
  • 从高值递减时,44085Hz之前返回正常,44084Hz会被返回为44085Hz,且差值随数值减小而增大。
  • 从低值递增时,39Hz以下返回0,40Hz返回1。

问题根源

SetPositionHzValue()采用的是对数映射逻辑,反向转换必须用对应的指数映射还原,但当前GetPositionsHzValue()的逻辑完全偏离了对数映射的逆运算,错误使用幂运算+线性缩放,导致结果偏差。

正确的GetPositionsHzValue()实现

function TAudioBezierCurve.GetPositionsHzValue: Double;
var
  LogOffsetValue: Double;
  LogMaxValue: Double;
begin
  // 边界情况处理:Position为0直接返回最小频率
  if Self.Position <= 0 then
  begin
    Result := Parent.GridFrequencyMin;
    Exit;
  end;
  // 边界情况处理:Position为1直接返回最大频率
  if Self.Position >= 1 then
  begin
    Result := Parent.GridFrequencyMax;
    Exit;
  end;

  // 复用正向转换的参数计算逻辑,保证一致性
  if Parent.GridFrequencyMin = 0 then
    LogOffsetValue := 0
  else
    LogOffsetValue := Log10(Parent.GridFrequencyMin);

  LogMaxValue := Log10(Parent.GridFrequencyMax) - LogOffsetValue;

  // 逆推对数映射:先还原到对数空间,再转换为实际Hz值
  Result := Power(10, Self.Position * LogMaxValue + LogOffsetValue);
end;

关键说明

  1. 逆运算逻辑:
    正向转换公式为Position = (log10(Hz) - LogOffset) / LogMax,逆推后得到:
    log10(Hz) = Position * LogMax + LogOffset
    再通过10的幂运算得到实际Hz值:Hz = 10^(Position * LogMax + LogOffset)
  2. 边界处理:
    对Position为0或1的情况直接返回对应边界频率,避免计算误差;若业务允许最小频率为0,需单独处理返回0的逻辑。
  3. 一致性保障:
    复用SetPositionHzValue中的参数计算逻辑,确保正向与反向转换使用完全相同的基准,避免参数不一致导致的偏差。

内容的提问来源于stack exchange,提问作者3delite

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:35:23