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:
2026-08-20 21:53:08 +03:00
parent 63e426cc0d
commit 8458ec65f4
10 changed files with 172 additions and 106 deletions
+5 -4
View File
@@ -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;
+2 -1
View File
@@ -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-бит |
+6
View File
@@ -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. */
+78
View File
@@ -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;
}
+7 -6
View File
@@ -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
View File
@@ -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;
} }
+37
View File
@@ -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
View File
@@ -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()). */
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;