No description
Find a file
2026-06-29 01:30:13 +00:00
README.md Обновить README.md 2026-06-29 01:30:13 +00:00

Digital Delay (Line6 DL4) — восстановленный алгоритм

Источник

  • Прошивка: 4×4 Delay, версия 1.03
  • Процессор: Motorola DSP56300
  • Ассемблерный дамп: адреса 0x0000e80x000110, 0x001d670x001d70, 0x0000530x000060, 0x00247d0x00247e, 0x0019c9, 0x00247c
  • Примечание: Восстановлен только эффект Digital Delay (Analog Delay, Tube Echo и другие не включены).

Структура алгоритма

graph LR
    A[Вход] --> B[Программная задержка<br>7×MAC];
    B --> C[Кольцевой буфер];
    C --> D[Обратная связь<br>0xA4];
    D --> B;
    C --> E[Смешивание Wet/Dry];
    E --> F[Выход];

Константы

Константа Значение Назначение
FEEDBACK_COEF 0xA4 (164) Коэффициент обратной связи (64%). Адреса: 0x0000f4, 0x0000fe
DELAY_PARAM 0x7D (125) Параметр программной задержки. Адрес: 0x001d67
BUFFER_SIZE 4096 Размер линии задержки (макс. ~85 мс при 48 кГц)

Полный код на C (восстановленный)

#include <stdint.h>

#define FEEDBACK_COEF   0xA4
#define DELAY_PARAM     0x7D
#define BUFFER_SIZE     4096

typedef struct {
    int16_t buffer[BUFFER_SIZE];
    uint16_t write_ptr;
    uint16_t read_ptr;
    int16_t feedback_gain;
    int16_t wet_gain;
    int16_t dry_gain;
} DigitalDelay;

static inline int16_t saturate(int32_t v) {
    if (v > 32767) return 32767;
    if (v < -32768) return -32768;
    return (int16_t)v;
}

/* Программная задержка: 7 × mac -y1 (0x001d670x001d70) */
static int16_t software_delay(int16_t input, int16_t coef) {
    int32_t acc = 0;
    for (int i = 0; i < 7; i++) {
        acc += (int32_t)input * coef;
        acc >>= 1;
    }
    return saturate(acc >> 8);
}

/* Цикл обработки (0x0000530x000060) */
static int16_t delay_loop(int16_t input, DigitalDelay *delay, int32_t *a, int32_t *b) {
    *a += (int32_t)input * delay->feedback_gain;   // macr +x0
    *a += input;                                    // add x0,a
    *a += *b;                                       // addr b,a
    *a -= *b;                                       // subr b,a
    *a += (int32_t)input * delay->feedback_gain;   // mpy +x0
    return saturate(*a >> 8);
}

/* Основной Digital Delay (0x0000e80x000110) */
int16_t process_digital_delay(int16_t input, DigitalDelay *delay, int32_t *a, int32_t *b) {
    int16_t delayed = delay->buffer[delay->read_ptr];

    // Обратная связь (0xA4)
    int32_t feedback = (int32_t)delayed * delay->feedback_gain;
    feedback >>= 8;

    // Запись в буфер (0x0000fb)
    int16_t new_sample = saturate(input + feedback);
    delay->buffer[delay->write_ptr] = new_sample;

    // Wet/Dry (0x000101, 0x00010b)
    int32_t wet = (int32_t)delayed * delay->wet_gain >> 8;
    int32_t dry = (int32_t)input * delay->dry_gain >> 8;
    int16_t output = saturate(wet + dry);

    // Обновление указателей
    delay->read_ptr = (delay->read_ptr + 1) % BUFFER_SIZE;
    delay->write_ptr = (delay->write_ptr + 1) % BUFFER_SIZE;

    return output;
}

/* Таймер (0x00247d0x00247e) */
void timer_init(uint16_t prescaler, uint16_t control) {
    // *(volatile uint16_t*)TPLR = prescaler;
    // *(volatile uint16_t*)RSMB1 = control;
}

/* Вызов из прошивки (0x0019c9, 0x00247c) */
int16_t call_delay(int16_t input, DigitalDelay *delay, int32_t *a, int32_t *b) {
    int16_t tmp = software_delay(input, DELAY_PARAM);   // 0x0019c9
    return process_digital_delay(tmp, delay, a, b);      // 0x00247c
}

/* Инициализация */
void init_digital_delay(DigitalDelay *delay, uint8_t feedback, uint8_t mix) {
    for (int i = 0; i < BUFFER_SIZE; i++) delay->buffer[i] = 0;
    delay->write_ptr = 0;
    delay->read_ptr = 0;
    delay->feedback_gain = (int16_t)(feedback - 128);   // 0xA4 → 36
    delay->wet_gain = mix;
    delay->dry_gain = (uint8_t)(255 - mix);
}

Таблица соответствий ассемблер → C

Адрес Ассемблер C-код
0x0000e8 mac -x1, n0, y... *a += input * feedback_gain
0x0000f4, 0x0000fe addr a,b #0xa4 feedback_gain = 0xA4
0x0000fb addr a,b buffer[write_ptr] = new_sample
0x0000fc sbc x,b delayed = buffer[read_ptr]
0x000101 mac +y1 wet = delayed * wet_gain
0x00010b add y0,a output = wet + dry
0x001d690x001d6f 7×mac -y1 цикл for (i=0;i<7;i++)
0x0000530x000060 macr, add, addr, subr, mpy delay_loop()
0x00247d movep TPLR timer_init(prescaler, ...)
0x0019c9 jsclr FUN... software_delay()
0x00247c jsr FUN... process_digital_delay()

Как это работает

  1. По прерыванию таймера вызывается функция call_delay(input, ...).
  2. Сначала выполняется программная задержка из 7 MAC-операций. Она создает микро-смещение и стабилизирует работу алгоритма.
  3. Затем запускается основной алгоритм:
    • Из кольцевого буфера (линии задержки) читается сэмпл по указателю read_ptr.
    • К нему применяется обратная связь с коэффициентом feedback_gain (164/256 ≈ 64%), и результат прибавляется к входному сэмплу.
    • Полученная сумма записывается обратно в буфер по указателю write_ptr.
    • Параллельно задержанный сэмпл умножается на wet_gain, а входной — на dry_gain. После они складываются, формируя выходной сигнал.
    • Указатели read_ptr и write_ptr инкрементируются, реализуя кольцевой буфер.
  4. Выходной сэмпл отправляется на ЦАП.

Весь цикл выполняется за фиксированное число тактов, что гарантирует обработку в реальном времени без пропусков.

Параметры и их влияние

  • Feedback (0xA4): Чем выше значение, тем больше повторений эха. 0xA4 = 64% — классическое значение для этого эффекта.
  • Wet/Dry (mix): Баланс между обработанным и сухим сигналом. Значение 128/128 = 50% является стандартным.
  • Размер буфера (4096): Определяет максимальную возможную задержку (≈85 мс при 48 кГц).
  • Частота дискретизации: Задается таймером (обычно 48 кГц или 44.1 кГц).