/* * cblstream — потоковое чтение с диска ОДНОВРЕМЕННО с воспроизведением * через CBL. Расчитан на быстрый носитель (HDD) — на floppy * захлёбывается (см. tests/cblwav, где поэтому весь клип грузится в * RAM заранее). speech30.pcm — ~39.6 c речи, моно 8-бит unsigned, * 21.875 кГц. * * ЕДИНСТВЕННЫЙ случай из трёх cbl-тестов, где приложению НУЖНО своё * кольцо (2026-07-07): read() — ESTEX-вызов, а fill()-callback CBL * зовётся ИЗ ISR, где ESTEX/BIOS под запретом (см. cbl.h). Поэтому * диск читается ТОЛЬКО в main (в собственное кольцо ring[]), а * fill_stream() лишь копирует уже готовые байты из ring в стейджинг и * пропихивает cbl_push_otir() — сам с диском не работает. */ #include #include #include #include #include #include #include #include #define RING_SIZE 2048u #define READ_CHUNK 1024u static uint8_t ring[RING_SIZE]; static uint16_t r_rd, r_wr, r_count; /* r_count: main под IRQ_DISABLE, fill() уже в ISR */ static uint8_t staging[128]; /* блок 8-бит моно */ static uint16_t ring_in = 0; static uint16_t ring_border = 0; static uint16_t ring_free(void) { IRQ_DISABLE(); uint16_t c = r_count; IRQ_ENABLE(); return (uint16_t)(RING_SIZE - c); } /* Вызывать ТОЛЬКО из main. */ static void ring_push(const void *src, uint16_t n) { const uint8_t *s = src; while (n) { uint16_t tail = (uint16_t)(RING_SIZE - r_wr); uint16_t chunk = n < tail ? n : tail; memcpy(ring + r_wr, s, chunk); r_wr = (uint16_t)((r_wr + chunk) % RING_SIZE); IRQ_DISABLE(); r_count += chunk; IRQ_ENABLE(); s += chunk; n = (uint16_t)(n - chunk); } } /* Зовётся ИЗ ISR — только копия из уже готового ring, никакого ESTEX. */ static int fill_stream(uint16_t n) { if (r_count < n) return 0; uint16_t tail = (uint16_t)(RING_SIZE - r_rd); if (tail >= n) { // memcpy(staging, ring + r_rd, n); cbl_push_otir(ring + r_rd, n); ring_in++; } else { memcpy(staging, ring + r_rd, tail); memcpy(staging + tail, ring, (uint16_t)(n - tail)); cbl_push_otir(staging, n); ring_border++; } r_rd = (uint16_t)((r_rd + n) % RING_SIZE); r_count = (uint16_t)(r_count - n); /* уже внутри ISR — IRQ и так замаскированы */ return 1; } int main(void) { int fd = open("speech30.pcm", O_RDONLY); if (fd < 0) { printf("open(speech30.pcm) failed: errno=%d\n", errno); return 1; } static uint8_t readbuf[READ_CHUNK]; uint32_t played = 0; uint8_t eof = 0; /* прогреть кольцо реальными данными ДО cbl_open()/puts() — иначе * пауза тишины на старте (тот же приём, что в cbltest.c/cblwav.c) */ while (!eof && ring_free() >= sizeof(readbuf)) { int got = read(fd, readbuf, sizeof(readbuf)); if (got <= 0) { eof = 1; break; } ring_push(readbuf, (uint16_t)got); played += (uint32_t)got; } if (cbl_open(CBL_FREQ_21K9, CBL_FMT_MONO8, CBL_PUMP_OTIR, CBL_UNDERRUN_APP, fill_stream) != 0) { printf("cbl_open failed: errno=%d\n", errno); close(fd); return 1; } puts("streaming speech30.pcm from disk (21.875 kHz, mono 8-bit)..."); while (!eof) { if (ring_free() >= sizeof(readbuf)) { int got = read(fd, readbuf, sizeof(readbuf)); if (got <= 0) { eof = 1; break; } ring_push(readbuf, (uint16_t)got); played += (uint32_t)got; } else { __asm__("halt"); /* ждём, пока ISR подъест кольцо */ } } close(fd); printf("streamed %lu bytes from disk\n", played); /* Ждём "почти полного" опустошения, а не точного RING_SIZE: если * общая длина потока не кратна 128, необслуженный хвост (<128 Б, * тот же квирк, что и в старом кольце libc — см. cbl.h) навечно * оставит r_count>0, и точное ожидание никогда бы не завершилось. */ while (ring_free() < RING_SIZE - 127u) __asm__("halt"); printf("requests: %u, underruns: %u (expect 0 during play)\n", cbl_requests(), cbl_underruns()); printf("ring: in: %u, border: %u\n", ring_in, ring_border); cbl_close(); puts("press any key (keyboard must work)..."); getch(); puts("cblstream done."); return 0; }