Jump to content
    

Настройка USB дескриптора UAC1 для аснихронной синхронизации.

7 minutes ago, WaRLoC said:

посмотрел на ачх

Раз в 20..40 секунд по факту вставляется сэмпл. Что там с ачх?

Share this post


Link to post
Share on other sites

Ресемплинг создает поток семплов из исходного путем пересчета и смещения фазы. Я попробовал линейную интерполяцию и кубическую. Раз в 20 секунд это у вас хорошо тайминги совпадают, я так подгонял частоту путем замены конденсаторов кварца, но переткнул в телефон, а там раз в полсекунды пошло. Конечно можно подогнать под конечное устройство на котором и будешь слушать, но дрейф параметров никто не отменял, хочется чтобы всегда...

Share this post


Link to post
Share on other sites

В худшем случае по документации - десять раз в секунду один сэмпл на 48000.

Телефон не сильно отличался. Может, то не кварц и был?

Share this post


Link to post
Share on other sites

В 07.04.2026 в 22:34, WaRLoC сказал:

Я попробовал линейную интерполяцию и кубическую. Раз в 20 секунд это у вас хорошо тайминги совпадают, я так подгонял частоту путем замены конденсаторов кварца, но переткнул в телефон, а там раз в полсекунды пошло. Конечно можно подогнать под конечное устройство на котором и будешь слушать, но дрейф параметров никто не отменял, хочется чтобы всегда...

Чтобы всегда, и работало при любой разнице частот: делаем ресэмплинг простейшей кусочно-линейной интерполяцией с переменным коэффициентом передискретизации. Не надо привязываться жёстко ни к каким разницам частот и ничего измерять: сначала определяем примерный (грубо) коэффициент передискретизации = K1 (как примерное соотношение частот); далее заполняем входной буфер сэмплов на половину и принимаем (начально) переменный коэффициент K2 = 1. Далее - передискритизируем с результирующим коэфф. = K1*K2. И непрерывно следим за уровнем заполненности и непрерывно пересчитываем K2 исходя из текущего уровня заполненности вх.буфера сэмплов.

Расчёт K2 можно делать например так: Разбиваем буфер входных сэмплов на 3 участка:

  • середина - при нахождении уровня заполненности в этом участке K2=1;
  • краевые участки - при движении уровня заполненности от центрального участка в глубь краевого к его границе, K2 растёт или падает от 1 (на границе с центральным участком) до некоей величины, задающей наклон характеристики; например возьмём величину наклона на краевых участках= 30%, тогда: K2 будет меняться от 1 до (1+0.3) - один край, и от 1 до (1-0.3) - другой край.

Таким образом, передискритизируя на переменный коэфф. K1*K2, выходная частота сэмплов будет автоматически подстраиваться под поток сэмплов на входе. И не важно с какой частотой он приходит. И даже если его частота плавает (дрейфует), этому алгоритму без разницы - прекрасно работает.

Share this post


Link to post
Share on other sites

Пример кода есть? Вы реализовывали?

Edited by GenaSPB

Share this post


Link to post
Share on other sites

Да, реализовывал. Давно у меня уже так работает.

Пример какого кода? Кусочно-линейного передискретизатора?

Вот он (Cortex-M4):

#define DS_FACTOR  3                      //показатель макс.даунсэмплинга
#define DS_MAX     ((1 << DS_FACTOR) - 1) //макс.даунсэмплинг (макс.возможное отношение частоты сэмплирования mp3-потока к DAC_FS_REAL)

//Параметры нелинейных участков на краях входного MPEG-буфера; 2 участка - в начале и в конце буфера
#define AOUT_slopeDist   0.24 //длина участков (должно быть <0.5 длины буфера)
#define AOUT_slopeFactor 0.09 //задаёт наклон участка (на данную величину изменяется коэфф. ресэмплинга на границах буфера)


//Передискретизация выходного потока сэмплов на частоту ЦАП.
//Формирует вых.поток сэмплов в smplBuf.data для передачи в ЦАП.
static int ReSmpl()
{
  CPU_SR_ALLOCATE();
  uint i0, i1, n;
  s16 *psr = &rsmplBuf[0];
  while (1) {
    i1 = sbufW;
    ENTR_CRT_SECTION();
    if ((int)(n = (i0 = sbufE) - i1 - 1) < 0) {
      n = ncell(smplBuf.data) - i1;
      if (!i0) n--;
    }
    if (!n) { //нет места для записи в smplBuf.data
      if (idleOutDis) {
        EXIT_CRT_SECTION();
        break;
      }
      switch (afaza) {
        case AFAZA_BREAK:
          EXIT_CRT_SECTION();
          return n;
        case AFAZA_PREBUF:
          n = ibufW;
          if ((int)(n -= ibufR) < 0) n += sizeof(inbuf);
          if ((n += decN) < prebufPorog) if (aoutOn >= OTYP_RT) break;
          afaza = AFAZA_PLAY;
          idleOut = 0;
          EXIT_CRT_SECTION();
          if (ptcb->prio == PRIO_TASK_AOUT_HIGH) continue;
          LEDon(PIN_LED_AOUT);
          BuzzerBeep(DevParam::BUZ_AOUT);
          OsTaskChangePrio(OS_PRIO_SELF, PRIO_TASK_AOUT_HIGH);
          continue;
      }
      idleOut = 1;
      EXIT_CRT_SECTION();
      if (ptcb->prio == PRIO_TASK_AOUT_HIGH) {
        LEDoff(PIN_LED_AOUT);
        OsTaskChangePrio(OS_PRIO_SELF, PRIO_TASK_AOUT_LOW);
      }
      OsFlagPendReset(&evOut, ms2tkt(TIME_IDLE_CHUNK));
      continue;
    }
    EXIT_CRT_SECTION();
    u32 *psw = &smplBuf.data[i1];
    u32 c, c0, c1, ca = rsmplX, cv = volumeK << 5;
    i0 = rsmplBSiz;
    #if !AOUT_ASM
    do {
      i1 = (ca = (ca >> DS_FACTOR) + rsmplVar) >> 32 - DS_FACTOR;
      psr += i1 * 2;
      c = (ca <<= DS_FACTOR) >> 17 | B31;
      c -= c << 16;
      u64 q = *(u64 *)psr;
      c0 = (u32)q & 0xFFFF | (u32)(q >> 32) << 16;
      c1 = (u32)q >> 16 | (u32)(q >> 32) & 0xFFFF0000;
      c0 = __USAT(__SMMLA(cv, __SMUADX(c, c0), B11), 12);
      c1 = __USAT(__SMMLA(cv, __SMUADX(c, c1), B11), 12);
      *psw++ = c0 << 4 | c1 << 20;
      if ((int)(i0 -= i1) < 0) break;
    } while (--n);
    #else
    u32 k = B11, i1 = rsmplVar;
    asm(
      "p01: ADD    %[ca], %[i1], %[ca], LSR %[shift0] \n"
      "     LSRS   R0, %[ca], %[shift1]               \n"
      "     ADD    %[psr], %[psr], R0, LSL #2         \n"
      "     LSLS   %[ca], %[ca], %[shift0]            \n"
      "     SUBS   %[i0], %[i0], R0                   \n"
      "     LDMIA  %[psr], {R0, R11}                  \n"
      "     PKHTB  R12, R11, R0, ASR #16              \n"
      "     PKHBT  R11, R0, R11, LSL #16              \n"
      "     LSR    R0, %[ca], #17                     \n"
      "     MOVT   R0, #8000h                         \n"
      "     SUB    R0, R0, R0, LSL #16                \n"
      "     SMUADX R11, R11, R0                       \n"
      "     SMUADX R12, R12, R0                       \n"
      "     SMMLA  R11, R11, %[cv], %[b11]            \n"
      "     SMMLA  R12, R12, %[cv], %[b11]            \n"
      "     PKHBT  R0, R12, R11, LSL #16              \n"
      "     USAT16 R0, #12, R0                        \n"
      "     LSL    R0, R0, #4                         \n"
      "     STR    R0, [%[psw]], #4                   \n"
      "     BMI    p02                                \n"
      "     SUBS   %[n], %[n], #1                     \n"
      "     BNE    p01                                \n"
      "p02:                                            "
      : [n]"+r"(n), [i0]"+r"(i0), [ca]"+r"(ca), [psr]"+r"(psr), [psw]"+r"(psw)
      : [cv]"r"(cv), [b11]"r"(k), [i1]"r"(i1), [shift0]"i"(DS_FACTOR), [shift1]"i"(32 - DS_FACTOR)
      : "cc", "memory", "R0", "R11", "R12");
    #endif
    rsmplBSiz = i0;
    rsmplX = ca;
    i1 = psw - &smplBuf.data[0];
    if (i1 >= ncell(smplBuf.data)) i1 = 0;
    sbufW = i1;
    if (n) break;
  }
  memmove(&rsmplBuf[0], psr, rsmplBSiz * 4 + 4 + (DS_MAX + 1) * 4);
  return n;
}

PS: Но сомневаюсь что это кому-то будет полезным. :mosking:

А алгоритм работы я расписал.

Share this post


Link to post
Share on other sites

3 hours ago, jcxz said:

Но сомневаюсь что это кому-то будет полезным

Нда... не очень. можно сказать совсем никак. Ну немного можно догадаться, как собственно интерполяция делается.

Share this post


Link to post
Share on other sites

5 часов назад, GenaSPB сказал:

Ну немного можно догадаться, как собственно интерполяция делается.

Зачем догадываться? В сети полно описаний, что такое "кусочно-линейная передискретизация". Зачем вы меня просили привести код частной реализации кусочно-линейного передискретизатора? С какой целью? Вы не способны прочитать описание в сети???

И вообще: как именно реализован алгоритм передискретизации - в данной теме не важно. Потому как речь шла вообще не о том. Речь шла об алгоритме автоматической подстройки частоты сэмплов.

Share this post


Link to post
Share on other sites

Алгоритмы известгы. Реализация для стерео 24..32 бита будет влитять на применимость.

У мння блок на которм моюет бпроизаводиться ресэмплинг - например 19*0.25 мс. 19*6 cэмплов. Попробую применить... хотя сейчас ошибок в демодуляции сигналов х.з. есть или нет... п на аудиозаписях в упор не слышу артефактов. Решение о встааке/удалении сэмпла принимается на основе оуенки размера очереди.

Edited by GenaSPB

Share this post


Link to post
Share on other sites

46 минут назад, GenaSPB сказал:

Реализация для стерео 24..32 бита будет влитять на применимость.

Приведённый выше исходник - для стерео 16 бит. Формат выходных сэмплов там - для вывода на сдвоенный 12-разрядный стерео-ЦАП STM32F4xx.

46 минут назад, GenaSPB сказал:

У мння блок на которм моюет бпроизаводиться ресэмплинг - например 19*0.25 мс. 19*6 cэмплов. Попробую применить... хотя сейчас ошибок в демодуляции сигналов х.з. есть или нет... п на аудиозаписях в упор не слышу артефактов. Решение о встааке/удалении сэмпла принимается на основе оуенки размера очереди.

Вариант со вставками/удалениями сэмплов - это у меня был первый вариант реализации. Затем переделал на этот - с кусочно-линейной передискретизацией. Теперь ни один сэмпл втуне не пропадает.  :new_russian:

 

PS: Была мысль попробовать реализовать какую-нить более сложную интерполяцию (сплайнами Акима или чё-нить подобное). Но подумал: "Я и сейчас на слух не слышу никаких артефактов. Как тогда смогу определить - улучшилось качество или нет?" Поэтому не стал делать.

Имхо: Если коэфф. передискретизации меньше 2, то кусочно-линейная использует все входные сэмплы для расчёта выходных сэмплов. И не сильно уступает в качестве более сложным интерполяциям. где K=FSвх/FSвых. Но с аудиофилами спорить не буду.

Share this post


Link to post
Share on other sites

В 08.04.2026 в 23:46, jcxz сказал:

Чтобы всегда, и работало при любой разнице частот: делаем ресэмплинг простейшей кусочно-линейной интерполяцией

это не мой метод))), в итоге сделал автоподстройку кварца HSE варикапами - держит с болтанкой +-2 сэмпла с периодом в минуту. более чем, фактически битперфект полученю спасибо))

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...