libc/cbl: две точки входа вместо underrun_mode — malloc больше не в резиденте
Линкер тянет .rel целиком, поэтому malloc/free, стоявшие в мёртвой ветке CBL_UNDERRUN_SILENCE внутри cbl_open, приезжали в резидент КАЖДОМУ приложению — включая те, что льют тишину сами и кучей не пользуются. Разведено: cbl_open(freq, fmt, pump, fill) — ничего не аллоцирует; cbl_open_silence(freq, fmt, pump, fill) — аллоцирует буфер тишины; _cbl_open_raw(...) — общее тело. Параметр underrun_mode из публичного API убран: режим задаёт выбор функции. cbl_close больше не зовёт free — иначе malloc возвращался бы тем же путём. Буфер тишины живёт до выхода из программы и переиспользуется; его размер запоминается, иначе открытие 16-бит после 8-бит писало бы memset'ом мимо выделенного куска. _cbl_open_raw указатель на буфер не трогает вовсе — иначе cbl_open после cbl_open_silence терял бы уже выделенную память. В дереве режим SILENCE не использовал никто: все три вызова (cblwav, cblstream, PoP) передавали CBL_UNDERRUN_APP. Итог для PoP: _CODE 24329 -> 23716 (−613 Б), свободно в резиденте 129 -> 747 Б. make size-check чистый (67 программ), звук в MAME проверен — насос отработал 696 запросов, pop_snd_ok/want = 1/1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,16 +48,17 @@ int pop_sfx_init(void) __banked
|
|||||||
*
|
*
|
||||||
* Параметры: 10 937,5 Гц, 8 бит моно — формат данных один в один
|
* Параметры: 10 937,5 Гц, 8 бит моно — формат данных один в один
|
||||||
* (sound_plan.md). OTIR, а не акселератор: тот нужен только для 16 бит.
|
* (sound_plan.md). OTIR, а не акселератор: тот нужен только для 16 бит.
|
||||||
* UNDERRUN_APP, а не SILENCE: тишину насос льёт СВОЮ (первый блок
|
* cbl_open, а не cbl_open_silence: тишину насос льёт СВОЮ (первый блок
|
||||||
* набора), и просить у libc буфер через malloc не надо — куча в
|
* набора), и просить у libc буфер через malloc не надо — куча в резиденте
|
||||||
* резиденте W2 тесная, и её отказ ронял бы весь звук. */
|
* W2 тесная, и её отказ ронял бы весь звук. Заодно так malloc вообще не
|
||||||
|
* приезжает в резидент (275 Б, см. libc/cbl/_cbl_open_raw.c). */
|
||||||
int pop_sfx_start(void) __banked
|
int pop_sfx_start(void) __banked
|
||||||
{
|
{
|
||||||
if (pop_snd_ok) return 0;
|
if (pop_snd_ok) return 0;
|
||||||
if (!pop_snd_want) return 0; /* выключено пользователем (Ctrl+S) */
|
if (!pop_snd_want) return 0; /* выключено пользователем (Ctrl+S) */
|
||||||
if (!snd_blk) return -1; /* набор не загружен */
|
if (!snd_blk) return -1; /* набор не загружен */
|
||||||
if (cbl_open(CBL_FREQ_10K9, CBL_FMT_MONO8, CBL_PUMP_OTIR,
|
if (cbl_open(CBL_FREQ_10K9, CBL_FMT_MONO8, CBL_PUMP_OTIR,
|
||||||
CBL_UNDERRUN_APP, pop_sfx_fill) != 0)
|
pop_sfx_fill) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
pop_snd_ok = 1;
|
pop_snd_ok = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -395,7 +395,8 @@ applications/PoP/docs/PORT_PLAN.md §2 — пользователь подтве
|
|||||||
|
|
||||||
| Сигнатура | Описание |
|
| Сигнатура | Описание |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `int cbl_open(freq_code, fmt, pump_mode, underrun_mode, fill)` | включить CBL (`0x90\|fmt\|freq`), зарегистрировать callback; 0 / -1+errno (EBUSY повтор, EINVAL — freq/fmt/pump_mode/underrun_mode плохие или OTIR+16-бит, ENOMEM — буфер тишины) |
|
| `int cbl_open(freq_code, fmt, pump_mode, fill)` | включить CBL (`0x90\|fmt\|freq`), зарегистрировать callback; недолив — забота приложения; **malloc не тянет**; 0 / -1+errno (EBUSY повтор, EINVAL — freq/fmt/pump_mode плохие или OTIR+16-бит) |
|
||||||
|
| `int cbl_open_silence(freq_code, fmt, pump_mode, fill)` | то же, но тишину при недоливе шлёт libc из своего буфера; ТОЛЬКО этот вариант тянет malloc (+ENOMEM). Буфер не освобождается — живёт до выхода |
|
||||||
| `void cbl_close(void)` | выключить CBL, снять хук, освободить буфер тишины (если был); висит на atexit |
|
| `void cbl_close(void)` | выключить CBL, снять хук, освободить буфер тишины (если был); висит на atexit |
|
||||||
| `void cbl_push_otir(const void *src, uint16_t n)` | пропихнуть n байт через `otir` в порт 0x4F; звать ИЗ fill() |
|
| `void cbl_push_otir(const void *src, uint16_t n)` | пропихнуть n байт через `otir` в порт 0x4F; звать ИЗ fill() |
|
||||||
| `void cbl_push_accel(const void *src, uint16_t n)` | то же акселератором (страница EMM 0xFD@0xC000); единственный путь для 16-бит |
|
| `void cbl_push_accel(const void *src, uint16_t n)` | то же акселератором (страница EMM 0xFD@0xC000); единственный путь для 16-бит |
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ extern uint8_t _cbl_port_refs; /* держатели bit7 помимо
|
|||||||
/* Запись в 16-битный порт управления 0x004E. */
|
/* Запись в 16-битный порт управления 0x004E. */
|
||||||
void _cbl_ctrl(uint8_t v);
|
void _cbl_ctrl(uint8_t v);
|
||||||
|
|
||||||
|
/* Общее тело обеих публичных точек входа (cbl_open / cbl_open_silence).
|
||||||
|
* Буфер тишины не трогает — им владеет cbl_open_silence.c; так malloc не
|
||||||
|
* приезжает в резидент приложениям, которые льют тишину сами. */
|
||||||
|
int _cbl_open_raw(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
|
||||||
|
uint8_t underrun_mode, cbl_fill_fn fill);
|
||||||
|
|
||||||
/* Залить весь аппаратный буфер (256 слотов) байтом тишины сразу после
|
/* Залить весь аппаратный буфер (256 слотов) байтом тишины сразу после
|
||||||
* включения — иначе первые 23 мс играет то, что лежало в буфере раньше.
|
* включения — иначе первые 23 мс играет то, что лежало в буфере раньше.
|
||||||
* Подробный разбор — в шапке _cbl_prime.c. */
|
* Подробный разбор — в шапке _cbl_prime.c. */
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* _cbl_open_raw — общее тело обеих точек входа CBL (cbl_open и
|
||||||
|
* cbl_open_silence). Проверяет параметры, вешает насос и включает железо.
|
||||||
|
*
|
||||||
|
* ПОЧЕМУ ТОЧЕК ВХОДА ДВЕ, а не один параметр underrun_mode. Линкер тянет
|
||||||
|
* .rel ЦЕЛИКОМ, поэтому malloc/free, стоящие в ветке CBL_UNDERRUN_SILENCE,
|
||||||
|
* приезжали в резидент КАЖДОМУ приложению — даже тому, которое льёт тишину
|
||||||
|
* само и кучей не пользуется вовсе. Это 275 Б кода плюс вся область кучи;
|
||||||
|
* в huge-режиме, где под резидент отведено 0x4100..0xBB00, такая мелочь
|
||||||
|
* заметна (у PoP оставалось 129 Б свободных). Разведя аллокацию в отдельный
|
||||||
|
* публичный модуль, мы отдаём выбор линкеру: зовёшь cbl_open — malloc не
|
||||||
|
* приезжает.
|
||||||
|
*
|
||||||
|
* Указатель на буфер тишины (_cbl_silence) здесь НЕ ТРОГАЕТСЯ: им владеет
|
||||||
|
* cbl_open_silence.c, который его и аллоцирует. В режиме APP насос на него
|
||||||
|
* не смотрит вовсе (условие в _cbl_pump_*), а обнуляй мы его тут — cbl_open
|
||||||
|
* после cbl_open_silence терял бы уже выделенный буфер, и следующая
|
||||||
|
* silence-сессия аллоцировала бы второй.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sprinter_exit.h>
|
||||||
|
#include "_cbl.h"
|
||||||
|
#include "../irq/_irq.h"
|
||||||
|
|
||||||
|
int _cbl_open_raw(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
|
||||||
|
uint8_t underrun_mode, cbl_fill_fn fill)
|
||||||
|
{
|
||||||
|
if (_irq_cbl_hook) { errno = EBUSY; return -1; }
|
||||||
|
if (freq_code < CBL_FREQ_7K8 || freq_code > CBL_FREQ_109K) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fmt & (uint8_t)~(CBL_FMT_MONO16 | CBL_FMT_STEREO8)) {
|
||||||
|
errno = EINVAL; /* биты вне 5/6 — неизвестный формат */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pump_mode != CBL_PUMP_OTIR && pump_mode != CBL_PUMP_ACCEL) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* ЗАПРЕТ CBL_PUMP_OTIR + 16-бит (подтверждено чтением исходника MAME
|
||||||
|
* sprinter.cpp): порт данных 0x4F ВСЕГДА кладёт байт как есть в один
|
||||||
|
* слот, не собирая пару байт в 16-бит значение и не сверяясь с
|
||||||
|
* cbl_mode16() вообще — это умеет только акселераторный путь (спец-
|
||||||
|
* страница 0xFD, do_accel_block). Комбинация принципиально не может
|
||||||
|
* звучать правильно, поэтому запрещена. */
|
||||||
|
if (pump_mode == CBL_PUMP_OTIR && (fmt & CBL_FMT_MONO16)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_irq_table_ref() != 0) return -1;
|
||||||
|
|
||||||
|
_cbl_block = (fmt & CBL_FMT_MONO16) ? 256u : 128u;
|
||||||
|
_cbl_reqs = 0;
|
||||||
|
_cbl_undr = 0;
|
||||||
|
_cbl_fill = fill;
|
||||||
|
_cbl_underrun_mode = underrun_mode;
|
||||||
|
|
||||||
|
static uint8_t atexit_armed;
|
||||||
|
if (!atexit_armed) {
|
||||||
|
atexit_armed = 1;
|
||||||
|
atexit(cbl_close); /* CBL обязан умолкнуть до шелла */
|
||||||
|
}
|
||||||
|
|
||||||
|
IRQ_DISABLE();
|
||||||
|
_irq_cbl_hook = (pump_mode == CBL_PUMP_ACCEL) ? _cbl_pump_accel : _cbl_pump_otir;
|
||||||
|
_cbl_ctrl((uint8_t)(0x90 | fmt | freq_code)); /* on + int + формат + частота */
|
||||||
|
/* ...и НЕМЕДЛЕННО забить буфер тишиной: железо пускает воспроизведение
|
||||||
|
* с нулевого слота той же записью в порт управления, а буфер хранит
|
||||||
|
* старое содержимое — без этого первые 23 мс уходит мусор. Заливка
|
||||||
|
* обязана быть внутри той же DI-скобки и сразу за включением (разбор —
|
||||||
|
* _cbl_prime.c). */
|
||||||
|
_cbl_prime((uint8_t)((fmt & CBL_FMT_MONO16) ? 0x00 : 0x80));
|
||||||
|
IRQ_ENABLE();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* cbl_close — выключить CBL, снять хук насоса, освободить буфер
|
* cbl_close — выключить CBL, снять хук насоса и отпустить IM2-таблицу.
|
||||||
* тишины (если был аллоцирован под CBL_UNDERRUN_SILENCE) и отпустить
|
* Идемпотентно; вызывается и из atexit.
|
||||||
* IM2-таблицу. Идемпотентно; вызывается и из atexit.
|
*
|
||||||
|
* БУФЕР ТИШИНЫ НЕ ОСВОБОЖДАЕТСЯ. Раньше здесь стоял free(_cbl_silence), и
|
||||||
|
* он один тянул malloc/free в резидент КАЖДОМУ приложению — включая те, что
|
||||||
|
* cbl_open_silence() не зовут вовсе. Буфер живёт до выхода из программы и
|
||||||
|
* переиспользуется при повторном открытии (см. cbl_open_silence.c).
|
||||||
*
|
*
|
||||||
* Порт 0x004E не гасится напрямую — вместо этого _cbl_port_sync()
|
* Порт 0x004E не гасится напрямую — вместо этого _cbl_port_sync()
|
||||||
* пересчитывает нужное значение: если gfx_wait_vsync() ещё держит
|
* пересчитывает нужное значение: если gfx_wait_vsync() ещё держит
|
||||||
@@ -9,7 +13,6 @@
|
|||||||
* синхронизации; иначе порт гасится полностью.
|
* синхронизации; иначе порт гасится полностью.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "_cbl.h"
|
#include "_cbl.h"
|
||||||
#include "../irq/_irq.h"
|
#include "../irq/_irq.h"
|
||||||
|
|
||||||
@@ -20,7 +23,5 @@ void cbl_close(void)
|
|||||||
_irq_cbl_hook = 0;
|
_irq_cbl_hook = 0;
|
||||||
IRQ_ENABLE();
|
IRQ_ENABLE();
|
||||||
_cbl_port_sync();
|
_cbl_port_sync();
|
||||||
free(_cbl_silence);
|
|
||||||
_cbl_silence = 0;
|
|
||||||
_irq_table_unref();
|
_irq_table_unref();
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-80
@@ -1,89 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
* cbl_open — включить CBL, зарегистрировать fill()-callback и один
|
* cbl_open — включить CBL, когда тишину при недоливе льёт САМО ПРИЛОЖЕНИЕ
|
||||||
* из ISR-насосов.
|
* (режим CBL_UNDERRUN_APP). Буфера не просит, поэтому НЕ ТЯНЕТ malloc —
|
||||||
|
* ради этого точки входа и разведены (см. шапку _cbl_open_raw.c).
|
||||||
*
|
*
|
||||||
* Управляющее слово: bit7 (CBL on) + bit4 (int enable) + fmt (bit6
|
* Если тишина нужна от libc — cbl_open_silence() в соседнем модуле.
|
||||||
* stereo, bit5 16-бит) + freq_code. Размер блока/буфер тишины
|
|
||||||
* зависят от fmt и underrun_mode (см. cbl.h). cbl_close вешается на
|
|
||||||
* atexit.
|
|
||||||
*
|
|
||||||
* ЗАПРЕТ CBL_PUMP_OTIR + 16-бит (подтверждено чтением исходника MAME
|
|
||||||
* sprinter.cpp): порт данных 0x4F ВСЕГДА кладёт байт как есть в один
|
|
||||||
* слот, не собирая пару байт в 16-бит значение и не сверяясь с
|
|
||||||
* cbl_mode16() вообще — это умеет только акселераторный путь (спец-
|
|
||||||
* страница 0xFD, do_accel_block). Комбинация принципиально не может
|
|
||||||
* звучать правильно, поэтому запрещена (EINVAL). Для 16-бит —
|
|
||||||
* CBL_PUMP_ACCEL.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sprinter_exit.h>
|
|
||||||
#include "_cbl.h"
|
#include "_cbl.h"
|
||||||
#include "../irq/_irq.h"
|
|
||||||
|
|
||||||
int cbl_open(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
|
int cbl_open(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
|
||||||
uint8_t underrun_mode, cbl_fill_fn fill)
|
cbl_fill_fn fill)
|
||||||
{
|
{
|
||||||
if (_irq_cbl_hook) { errno = EBUSY; return -1; }
|
return _cbl_open_raw(freq_code, fmt, pump_mode, CBL_UNDERRUN_APP, fill);
|
||||||
if (freq_code < CBL_FREQ_7K8 || freq_code > CBL_FREQ_109K) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (fmt & (uint8_t)~(CBL_FMT_MONO16 | CBL_FMT_STEREO8)) {
|
|
||||||
errno = EINVAL; /* биты вне 5/6 — неизвестный формат */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (pump_mode != CBL_PUMP_OTIR && pump_mode != CBL_PUMP_ACCEL) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (pump_mode == CBL_PUMP_OTIR && (fmt & CBL_FMT_MONO16)) {
|
|
||||||
errno = EINVAL; /* OTIR не может собрать 16-бит сэмпл — только ACCEL */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (underrun_mode != CBL_UNDERRUN_APP && underrun_mode != CBL_UNDERRUN_SILENCE) {
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t block = (fmt & CBL_FMT_MONO16) ? 256u : 128u;
|
|
||||||
|
|
||||||
uint8_t *silence = 0;
|
|
||||||
if (underrun_mode == CBL_UNDERRUN_SILENCE) {
|
|
||||||
silence = malloc(block);
|
|
||||||
if (!silence) { errno = ENOMEM; return -1; }
|
|
||||||
memset(silence, (fmt & CBL_FMT_MONO16) ? 0x00 : 0x80, block);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_irq_table_ref() != 0) {
|
|
||||||
free(silence);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cbl_block = block;
|
|
||||||
_cbl_reqs = 0;
|
|
||||||
_cbl_undr = 0;
|
|
||||||
_cbl_fill = fill;
|
|
||||||
_cbl_underrun_mode = underrun_mode;
|
|
||||||
_cbl_silence = silence;
|
|
||||||
|
|
||||||
static uint8_t atexit_armed;
|
|
||||||
if (!atexit_armed) {
|
|
||||||
atexit_armed = 1;
|
|
||||||
atexit(cbl_close); /* CBL обязан умолкнуть до шелла */
|
|
||||||
}
|
|
||||||
|
|
||||||
IRQ_DISABLE();
|
|
||||||
_irq_cbl_hook = (pump_mode == CBL_PUMP_ACCEL) ? _cbl_pump_accel : _cbl_pump_otir;
|
|
||||||
_cbl_ctrl((uint8_t)(0x90 | fmt | freq_code)); /* on + int + формат + частота */
|
|
||||||
/* ...и НЕМЕДЛЕННО забить буфер тишиной: железо пускает воспроизведение
|
|
||||||
* с нулевого слота той же записью в порт управления, а буфер хранит
|
|
||||||
* старое содержимое — без этого первые 23 мс уходит мусор. Заливка
|
|
||||||
* обязана быть внутри той же DI-скобки и сразу за включением (разбор —
|
|
||||||
* _cbl_prime.c). */
|
|
||||||
_cbl_prime((uint8_t)((fmt & CBL_FMT_MONO16) ? 0x00 : 0x80));
|
|
||||||
IRQ_ENABLE();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* cbl_open_silence — то же, что cbl_open, но при недоливе тишину шлёт САМА
|
||||||
|
* libc: буфер под неё аллоцируется здесь. Именно этот модуль тянет malloc,
|
||||||
|
* поэтому приложения на cbl_open() за него не платят.
|
||||||
|
*
|
||||||
|
* БУФЕР НЕ ОСВОБОЖДАЕТСЯ. cbl_close() его не трогает намеренно: free()
|
||||||
|
* в cbl_close вернул бы malloc в резидент всем подряд — ровно то, от чего
|
||||||
|
* уходили. Потеря невелика (128 или 256 байт), а при повторном открытии
|
||||||
|
* буфер переиспользуется; окончательно память возвращает выход из
|
||||||
|
* программы.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "_cbl.h"
|
||||||
|
|
||||||
|
/* Сколько байт под буфером сейчас лежит. Помнить обязательно: у 8-бит
|
||||||
|
* формата блок 128, у 16-бит — 256, и открытие 16-бит ПОСЛЕ 8-бит писало бы
|
||||||
|
* memset'ом мимо выделенного куска. */
|
||||||
|
static uint16_t silence_cap;
|
||||||
|
|
||||||
|
int cbl_open_silence(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
|
||||||
|
cbl_fill_fn fill)
|
||||||
|
{
|
||||||
|
uint16_t block = (fmt & CBL_FMT_MONO16) ? 256u : 128u;
|
||||||
|
uint8_t quiet = (fmt & CBL_FMT_MONO16) ? 0x00 : 0x80;
|
||||||
|
|
||||||
|
if (silence_cap < block) {
|
||||||
|
uint8_t *p = malloc(block);
|
||||||
|
if (!p) { errno = ENOMEM; return -1; }
|
||||||
|
_cbl_silence = p; /* прежний кусок не освобождаем — см. шапку */
|
||||||
|
silence_cap = block;
|
||||||
|
}
|
||||||
|
memset(_cbl_silence, quiet, block);
|
||||||
|
return _cbl_open_raw(freq_code, fmt, pump_mode, CBL_UNDERRUN_SILENCE, fill);
|
||||||
|
}
|
||||||
+29
-13
@@ -44,13 +44,14 @@
|
|||||||
* кладёт байт как есть в один слот, не собирая пару байт в 16-бит
|
* кладёт байт как есть в один слот, не собирая пару байт в 16-бит
|
||||||
* значение — это умеет только акселераторный путь (страница 0xFD).
|
* значение — это умеет только акселераторный путь (страница 0xFD).
|
||||||
*
|
*
|
||||||
* Поведение при недоливе — CBL_UNDERRUN_* (4-й параметр cbl_open):
|
* Поведение при недоливе задаётся ВЫБОРОМ ТОЧКИ ВХОДА (cbl_open /
|
||||||
|
* cbl_open_silence), а не параметром:
|
||||||
* CBL_UNDERRUN_APP (по умолчанию, 0) — недолив не наша забота;
|
* CBL_UNDERRUN_APP (по умолчанию, 0) — недолив не наша забота;
|
||||||
* в CBL доигрывает то, что уже лежит в его буфере (никакого
|
* в CBL доигрывает то, что уже лежит в его буфере (никакого
|
||||||
* буфера тишины не аллоцируется).
|
* буфера тишины не аллоцируется).
|
||||||
* CBL_UNDERRUN_SILENCE (1) — насос сам пропихивает тишину при
|
* CBL_UNDERRUN_SILENCE (1) — насос сам пропихивает тишину при
|
||||||
* недоливе; буфер тишины (128/256 Б по формату) аллоцируется
|
* недоливе; буфер тишины (128/256 Б по формату) аллоцируется
|
||||||
* malloc'ом ВНУТРИ cbl_open(), только в этом режиме.
|
* malloc'ом внутри cbl_open_silence(), только в этом режиме.
|
||||||
* cbl_underruns() считает недоливы в ОБОИХ режимах — это диагностика,
|
* cbl_underruns() считает недоливы в ОБОИХ режимах — это диагностика,
|
||||||
* поведение не меняет.
|
* поведение не меняет.
|
||||||
*
|
*
|
||||||
@@ -89,8 +90,10 @@
|
|||||||
#define CBL_PUMP_ACCEL 1 /* cbl_push_accel() — единственный путь для 16-бит */
|
#define CBL_PUMP_ACCEL 1 /* cbl_push_accel() — единственный путь для 16-бит */
|
||||||
|
|
||||||
/* Поведение при недоливе (fill() вернул 0 или не задан). */
|
/* Поведение при недоливе (fill() вернул 0 или не задан). */
|
||||||
#define CBL_UNDERRUN_APP 0 /* по умолчанию: не наша забота, буфер тишины не аллоцируется */
|
/* Внутренние коды режима недолива: приложение их не передаёт (режим задаёт
|
||||||
#define CBL_UNDERRUN_SILENCE 1 /* насос сам шлёт тишину; буфер аллоцируется в cbl_open() */
|
* выбор cbl_open / cbl_open_silence), но насос по ним и различает поведение. */
|
||||||
|
#define CBL_UNDERRUN_APP 0 /* не наша забота, буфер тишины не аллоцируется */
|
||||||
|
#define CBL_UNDERRUN_SILENCE 1 /* насос сам шлёт тишину из буфера libc */
|
||||||
|
|
||||||
/* fill(n): пропихнуть n байт (через cbl_push_otir/accel) прямо из
|
/* fill(n): пропихнуть n байт (через cbl_push_otir/accel) прямо из
|
||||||
* данных приложения. Вызывается ИЗ ISR — обязан быть быстрым (без
|
* данных приложения. Вызывается ИЗ ISR — обязан быть быстрым (без
|
||||||
@@ -98,17 +101,30 @@
|
|||||||
* данных сейчас (недолив). */
|
* данных сейчас (недолив). */
|
||||||
typedef int (*cbl_fill_fn)(uint16_t n);
|
typedef int (*cbl_fill_fn)(uint16_t n);
|
||||||
|
|
||||||
/* Включить CBL с частотой CBL_FREQ_*, форматом CBL_FMT_*, способом
|
/* Включить CBL с частотой CBL_FREQ_*, форматом CBL_FMT_* и способом выдачи
|
||||||
* выдачи CBL_PUMP_* и поведением при недоливе CBL_UNDERRUN_*; fill —
|
* CBL_PUMP_*; fill — callback, зовущийся из ISR за новым блоком (может быть
|
||||||
* callback, зовущийся из ISR за новым блоком (может быть NULL, тогда
|
* NULL, тогда недолив — на каждом запросе). Недолив НЕ наша забота
|
||||||
* недолив — на каждом запросе). 0 или -1 + errno (EBUSY — уже
|
* (CBL_UNDERRUN_APP): железо доиграет по кругу неактивную половину, и если
|
||||||
* открыт; EINVAL — плохой код частоты/формат/pump/underrun_mode, или
|
* это слышно — приложение обязано отдавать тишину само.
|
||||||
* не-W2 режим; ENOMEM — не хватило памяти под буфер тишины). */
|
*
|
||||||
|
* 0 или -1 + errno (EBUSY — уже открыт; EINVAL — плохой код частоты, формат,
|
||||||
|
* pump, или не-W2 режим).
|
||||||
|
*
|
||||||
|
* ТОЧКИ ВХОДА ДВЕ, и это не косметика: cbl_open() не аллоцирует ничего и
|
||||||
|
* потому НЕ ТЯНЕТ malloc в резидент. За тишиной от libc — cbl_open_silence()
|
||||||
|
* ниже, и malloc приезжает только тому, кто её позвал. */
|
||||||
int cbl_open(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
|
int cbl_open(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
|
||||||
uint8_t underrun_mode, cbl_fill_fn fill);
|
cbl_fill_fn fill);
|
||||||
|
|
||||||
/* Выключить CBL, отпустить IM2-таблицу, освободить буфер тишины (если
|
/* То же, но при недоливе тишину шлёт САМА libc (CBL_UNDERRUN_SILENCE):
|
||||||
* был). Идемпотентно. */
|
* буфер под неё аллоцируется здесь. Добавляет ENOMEM к кодам ошибок.
|
||||||
|
* Буфер живёт до конца программы и переиспользуется при повторном открытии
|
||||||
|
* — cbl_close() его не освобождает намеренно (иначе free вернул бы malloc
|
||||||
|
* в резидент всем подряд). */
|
||||||
|
int cbl_open_silence(uint8_t freq_code, uint8_t fmt, uint8_t pump_mode,
|
||||||
|
cbl_fill_fn fill);
|
||||||
|
|
||||||
|
/* Выключить CBL и отпустить IM2-таблицу. Идемпотентно. */
|
||||||
void cbl_close(void);
|
void cbl_close(void);
|
||||||
|
|
||||||
/* Пропихнуть n байт из src прямо в CBL (звать ИЗ fill()). */
|
/* Пропихнуть n байт из src прямо в CBL (звать ИЗ fill()). */
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ int main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cbl_open(CBL_FREQ_21K9, CBL_FMT_MONO8, CBL_PUMP_OTIR,
|
if (cbl_open(CBL_FREQ_21K9, CBL_FMT_MONO8, CBL_PUMP_OTIR,
|
||||||
CBL_UNDERRUN_APP, fill_stream) != 0) {
|
fill_stream) != 0) {
|
||||||
printf("cbl_open failed: errno=%d\n", errno);
|
printf("cbl_open failed: errno=%d\n", errno);
|
||||||
close(fd);
|
close(fd);
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ int main(void)
|
|||||||
done = 0;
|
done = 0;
|
||||||
|
|
||||||
if (cbl_open(CBL_FREQ_21K9, CBL_FMT_MONO8, CBL_PUMP_OTIR,
|
if (cbl_open(CBL_FREQ_21K9, CBL_FMT_MONO8, CBL_PUMP_OTIR,
|
||||||
CBL_UNDERRUN_APP, fill_speech) != 0) {
|
fill_speech) != 0) {
|
||||||
printf("cbl_open failed: errno=%d\n", errno);
|
printf("cbl_open failed: errno=%d\n", errno);
|
||||||
mem_free_block(blk);
|
mem_free_block(blk);
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user