GenaSPB 36 April 7 Posted April 7 · Report post 7 minutes ago, WaRLoC said: посмотрел на ачх Раз в 20..40 секунд по факту вставляется сэмпл. Что там с ачх? Quote Share this post Link to post Share on other sites More sharing options...
WaRLoC 0 April 7 Posted April 7 · Report post Ресемплинг создает поток семплов из исходного путем пересчета и смещения фазы. Я попробовал линейную интерполяцию и кубическую. Раз в 20 секунд это у вас хорошо тайминги совпадают, я так подгонял частоту путем замены конденсаторов кварца, но переткнул в телефон, а там раз в полсекунды пошло. Конечно можно подогнать под конечное устройство на котором и будешь слушать, но дрейф параметров никто не отменял, хочется чтобы всегда... Quote Share this post Link to post Share on other sites More sharing options...
GenaSPB 36 April 7 Posted April 7 · Report post В худшем случае по документации - десять раз в секунду один сэмпл на 48000. Телефон не сильно отличался. Может, то не кварц и был? Quote Share this post Link to post Share on other sites More sharing options...
jcxz 363 April 8 Posted April 8 · Report post В 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, выходная частота сэмплов будет автоматически подстраиваться под поток сэмплов на входе. И не важно с какой частотой он приходит. И даже если его частота плавает (дрейфует), этому алгоритму без разницы - прекрасно работает. Quote Share this post Link to post Share on other sites More sharing options...
GenaSPB 36 April 9 Posted April 9 (edited) · Report post Пример кода есть? Вы реализовывали? Edited April 9 by GenaSPB Quote Share this post Link to post Share on other sites More sharing options...
jcxz 363 April 9 Posted April 9 · Report post Да, реализовывал. Давно у меня уже так работает. Пример какого кода? Кусочно-линейного передискретизатора? Вот он (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: Но сомневаюсь что это кому-то будет полезным. А алгоритм работы я расписал. Quote Share this post Link to post Share on other sites More sharing options...
GenaSPB 36 April 9 Posted April 9 · Report post 3 hours ago, jcxz said: Но сомневаюсь что это кому-то будет полезным Нда... не очень. можно сказать совсем никак. Ну немного можно догадаться, как собственно интерполяция делается. 1 Quote Share this post Link to post Share on other sites More sharing options...
jcxz 363 April 9 Posted April 9 · Report post 5 часов назад, GenaSPB сказал: Ну немного можно догадаться, как собственно интерполяция делается. Зачем догадываться? В сети полно описаний, что такое "кусочно-линейная передискретизация". Зачем вы меня просили привести код частной реализации кусочно-линейного передискретизатора? С какой целью? Вы не способны прочитать описание в сети??? И вообще: как именно реализован алгоритм передискретизации - в данной теме не важно. Потому как речь шла вообще не о том. Речь шла об алгоритме автоматической подстройки частоты сэмплов. Quote Share this post Link to post Share on other sites More sharing options...
GenaSPB 36 April 9 Posted April 9 (edited) · Report post Алгоритмы известгы. Реализация для стерео 24..32 бита будет влитять на применимость. У мння блок на которм моюет бпроизаводиться ресэмплинг - например 19*0.25 мс. 19*6 cэмплов. Попробую применить... хотя сейчас ошибок в демодуляции сигналов х.з. есть или нет... п на аудиозаписях в упор не слышу артефактов. Решение о встааке/удалении сэмпла принимается на основе оуенки размера очереди. Edited April 9 by GenaSPB Quote Share this post Link to post Share on other sites More sharing options...
jcxz 363 April 9 Posted April 9 · Report post 46 минут назад, GenaSPB сказал: Реализация для стерео 24..32 бита будет влитять на применимость. Приведённый выше исходник - для стерео 16 бит. Формат выходных сэмплов там - для вывода на сдвоенный 12-разрядный стерео-ЦАП STM32F4xx. 46 минут назад, GenaSPB сказал: У мння блок на которм моюет бпроизаводиться ресэмплинг - например 19*0.25 мс. 19*6 cэмплов. Попробую применить... хотя сейчас ошибок в демодуляции сигналов х.з. есть или нет... п на аудиозаписях в упор не слышу артефактов. Решение о встааке/удалении сэмпла принимается на основе оуенки размера очереди. Вариант со вставками/удалениями сэмплов - это у меня был первый вариант реализации. Затем переделал на этот - с кусочно-линейной передискретизацией. Теперь ни один сэмпл втуне не пропадает. PS: Была мысль попробовать реализовать какую-нить более сложную интерполяцию (сплайнами Акима или чё-нить подобное). Но подумал: "Я и сейчас на слух не слышу никаких артефактов. Как тогда смогу определить - улучшилось качество или нет?" Поэтому не стал делать. Имхо: Если коэфф. передискретизации меньше 2, то кусочно-линейная использует все входные сэмплы для расчёта выходных сэмплов. И не сильно уступает в качестве более сложным интерполяциям. где K=FSвх/FSвых. Но с аудиофилами спорить не буду. Quote Share this post Link to post Share on other sites More sharing options...
WaRLoC 0 April 17 Posted April 17 · Report post В 08.04.2026 в 23:46, jcxz сказал: Чтобы всегда, и работало при любой разнице частот: делаем ресэмплинг простейшей кусочно-линейной интерполяцией это не мой метод))), в итоге сделал автоподстройку кварца HSE варикапами - держит с болтанкой +-2 сэмпла с периодом в минуту. более чем, фактически битперфект полученю спасибо)) Quote Share this post Link to post Share on other sites More sharing options...