rewrite update_task

This commit is contained in:
Steven Dan
2026-03-20 16:07:29 +08:00
parent ec585a1868
commit c36a6360c9
2 changed files with 320 additions and 329 deletions

View File

@@ -26,6 +26,11 @@ extern "C" {
#include "sw_pll.h"
}
#include "dnr_dsp_buf.h"
extern "C" {
#include "roleswitchflag.h"
}
#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) && (XUA_I2S_N_BITS != 32)
#warning ADC only supports TDM operation at 32 bits
#endif
@@ -75,6 +80,8 @@ unsigned g_request_filter_mode = -1; // -1表示无请求0-7=滤波器模式
unsigned g_request_game_mode = -1;
#if EQ_EN
extern unsigned g_request_eq_mode, g_new_eq_mode;
extern unsigned g_force_request_eq_mode_change;
extern unsigned g_force_eq_mode_change;
#endif
#define LED_OFF 0x7F // 修改清除最高位只保留低7位
@@ -93,6 +100,7 @@ port p_scl = PORT_I2C_SCL;
port p_sda = PORT_I2C_SDA;
port p_adc_rst = PORT_ADC_RST;
port p_ctrl = PORT_GPI;
on tile[0]: port p_ctrl_keys = XS1_PORT_8C;
/* Board setup for XU316 MC Audio (1v1) */
void board_setup()
@@ -218,7 +226,41 @@ int i2c_codec_mic_vol_down(void)
}
// tile 0
extern void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol);
#define KEY_POLLING_INTERVAL 10000000L // 100ms polling interval
#ifndef HID_MAX_DATA_BYTES
#define HID_MAX_DATA_BYTES (64)
#endif
#ifndef MUTE_ON
#define MUTE_ON 1
#define MUTE_OFF 0
#endif
#define MODE_STEREO_2K UAC1_IN_FLAG
#define MODE_SPATIAL_GAME COAX_IN_FLAG
#define MODE_SPATIAL_MOVIE OPT_IN_FLAG
#define MODE_UAC1 USB_IN_FLAG
#define OS_WIN 1
#define OS_OTHERS 2
extern unsigned g_host_os;
extern unsigned g_windows_detect_done;
extern unsigned g_mic_vol_cmd_pending;
extern unsigned g_uac_vol;
extern unsigned g_mute_on_off_t0;
extern unsigned g_game_mode;
extern unsigned hidSendData[];
extern "C" {
void update_button(unsigned char b);
void hidSetChangePending(unsigned int id);
void save_value(unsigned char *path, unsigned char value);
unsigned char load_value(unsigned char *path);
void device_reboot(void);
void xc_chan_out_byte(chanend c, unsigned char b);
}
void codec_init(void)
{
@@ -343,6 +385,278 @@ void codec_init(void)
}
}
void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol)
{
timer tmr;
uint32_t t_val;
static int uac_vol = 0, last_uac_vol = 0;
#if EQ_EN
static unsigned eq_sync_counter = 0;
#endif
unsigned port_ctrl_keys = 0, curr_ctrl_keys = 0, last_ctrl_keys = 0, keys_changed = 0;
int debounce_cnt = 0;
static unsigned isMute = 0;
unsigned char tmp = 0;
unsigned hidData0;
delay_milliseconds(20);
codec_init();
delay_milliseconds(500);
debug_printf("button task start\n");
unsigned char saved_mode;
unsigned char path[] = "game_mode";
unsigned host_os;
GET_SHARED_GLOBAL(host_os, g_host_os);
saved_mode = load_value(path);
debug_printf("Loaded game_mode from flash: %d\n", saved_mode);
#if defined(UAC1_MODE)
if (saved_mode == 255) {
saved_mode = 0;
save_value(path, saved_mode);
debug_printf("Saved game_mode to flash: %d\n", saved_mode);
}
#else
if (saved_mode == 255) {
#if defined(STEREO_2K)
saved_mode = 0;
#elif defined(SPATIAL_GAME)
saved_mode = 2;
#elif defined(SPATIAL_MOVIE)
saved_mode = 3;
#endif
save_value(path, saved_mode);
debug_printf("Saved game_mode to flash: %d\n", saved_mode);
}
#endif
printf("host_os: %d\n", host_os);
#if 0
if ((host_os == OS_OTHERS) || (host_os == 0)) {
#if !UAC1_MODE
printf("Detected non-Windows OS (OS_OTHERS), switching to UAC1 mode\n");
SetRoleSwitchFlag(MODE_UAC1);
debug_printf("Set role switch flag: 0x%04X (UAC1), device will restart\n", MODE_UAC1);
device_reboot();
while(1);
#endif
}
#endif
#if UAC1_MODE == 1
if (host_os == OS_WIN) {
printf("Detected Windows OS (OS_WIN) saved_mode: %d\n", saved_mode);
#if IR_SWITCHING_MODE
if (saved_mode == 0) {
SetRoleSwitchFlag(MODE_STEREO_2K);
} else {
SetRoleSwitchFlag(MODE_SPATIAL_GAME);
}
#else
{
switch (saved_mode) {
case 0:
SetRoleSwitchFlag(MODE_STEREO_2K);
break;
case 1:
SetRoleSwitchFlag(MODE_STEREO_2K);
break;
case 2:
SetRoleSwitchFlag(MODE_SPATIAL_GAME);
break;
case 3:
SetRoleSwitchFlag(MODE_SPATIAL_MOVIE);
break;
default:
SetRoleSwitchFlag(MODE_STEREO_2K);
break;
}
debug_printf("Switch to saved mode: %d, device will restart.\n", saved_mode);
delay_milliseconds(200);
device_reboot();
while (1);
}
#endif
}
#endif
#if defined(STEREO_2K)
if ((saved_mode != 255) && (saved_mode != 0) && (saved_mode != 1)) {
saved_mode = 0;
}
#endif
#if defined(SPATIAL_GAME)
if ((saved_mode != 255) && (saved_mode != 2)) {
saved_mode = 2;
}
#endif
#if defined(SPATIAL_MOVIE)
if ((saved_mode != 255) && (saved_mode != 3)) {
saved_mode = 3;
}
#endif
g_windows_detect_done = 1;
SET_SHARED_GLOBAL(g_game_mode, saved_mode);
debug_printf("Set new g_game_mode to %d\n", saved_mode);
// 音频模式传输命令byte协议与hid_button_task的chan_in_byte对应不带END token
xc_chan_out_byte(cc_mic_level, 0xFC);
xc_chan_out_byte(cc_mic_level, (unsigned char)saved_mode);
debug_printf("Sent audio_mode %d to hid_button_task via cc_mic_level channel\n", saved_mode);
p_ctrl_keys :> port_ctrl_keys;
if ((port_ctrl_keys & KEY_MUTE) == 0) {
debug_printf("unmuted\n");
isMute = 0;
SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_OFF);
} else {
debug_printf("muted\n");
isMute = 1;
SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_ON);
}
last_ctrl_keys = (port_ctrl_keys & KEY_BITS);
last_ctrl_keys ^= KEY_MUTE;
tmr :> t_val;
t_val += KEY_POLLING_INTERVAL;
while (1) {
select {
case c_hidSendData :> hidData0:
{
hidSendData[0] = hidData0;
if (hidData0 == 0xffffffff) {
unsigned new_mode;
c_hidSendData :> new_mode;
debug_printf("received new_mode from tile1: %d\n", new_mode);
if (new_mode > 3) {
new_mode = 0;
}
SET_SHARED_GLOBAL(g_game_mode, new_mode);
{
unsigned char path2[] = "game_mode";
save_value(path2, (unsigned char)new_mode);
}
} else {
#if (HID_CONTROLS == 1)
for (int i = 1; i < (HID_MAX_DATA_BYTES/4); i++) {
c_hidSendData :> hidSendData[i];
}
hidSetChangePending(1);
#endif
}
}
break;
case tmr when timerafter(t_val) :> void:
t_val += KEY_POLLING_INTERVAL;
{
unsigned curr;
p_ctrl_keys :> curr;
curr_ctrl_keys = curr & KEY_BITS;
// 防抖连续2次检测到相同变化才响应
if (curr_ctrl_keys != last_ctrl_keys) {
debounce_cnt++;
if (debounce_cnt >= 2) {
debounce_cnt = 0;
keys_changed = curr_ctrl_keys ^ last_ctrl_keys;
tmp = 0;
switch (keys_changed) {
case KEY_MIC_VOL_DN:
case KEY_MIC_VOL_UP:
if ((isMute == 0) && ((curr_ctrl_keys & KEY_MIC_VOL_DN) == 0)) {
tmp = KEY_MIC_VOL_DN;
debug_printf("KEY_MIC_VOL_DN pressed\n");
} else {
if ((isMute == 0) && ((curr_ctrl_keys & KEY_MIC_VOL_UP) == 0)) {
tmp = KEY_MIC_VOL_UP;
debug_printf("KEY_MIC_VOL_UP pressed\n");
}
}
if (tmp) {
g_mic_vol_cmd_pending = (unsigned)tmp;
}
break;
case KEY_MUTE:
if ((curr_ctrl_keys & KEY_MUTE) == 0) {
isMute = 0;
SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_OFF);
xc_chan_out_byte(cc_mic_level, UNMUTED_MIC);
debug_printf("MUTE: unmuted\n");
} else {
isMute = 1;
SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_ON);
xc_chan_out_byte(cc_mic_level, MUTED_MIC);
debug_printf("MUTE: muted\n");
}
break;
#if (HID_CONTROLS == 1)
case KEY_PLAY_VOL_DN:
if ((curr_ctrl_keys & KEY_PLAY_VOL_DN) == 0) {
debug_printf("KEY_PLAY_VOL_DN pressed\n");
update_button(HID_CONTROL_VOLDN);
}
break;
case KEY_PLAY_VOL_UP:
if ((curr_ctrl_keys & KEY_PLAY_VOL_UP) == 0) {
update_button(HID_CONTROL_VOLUP);
}
break;
#endif
default:
break;
}
last_ctrl_keys = curr_ctrl_keys;
}
} else {
debounce_cnt = 0;
}
GET_SHARED_GLOBAL(uac_vol, g_uac_vol);
if (uac_vol != last_uac_vol) {
unsigned conv_vol = (0xffffffff - uac_vol + 1) >> 8;
c_uac_vol <: conv_vol;
last_uac_vol = uac_vol;
}
#if EQ_EN
{
unsigned req_eq, new_eq, force_req;
GET_SHARED_GLOBAL(req_eq, g_request_eq_mode);
GET_SHARED_GLOBAL(new_eq, g_new_eq_mode);
GET_SHARED_GLOBAL(force_req, g_force_request_eq_mode_change);
if (new_eq != req_eq) {
SET_SHARED_GLOBAL(g_new_eq_mode, req_eq);
}
if (force_req == 1) {
debug_printf("force request mode change\n");
SET_SHARED_GLOBAL(g_force_eq_mode_change, 1);
SET_SHARED_GLOBAL(g_force_request_eq_mode_change, 0);
}
}
eq_sync_counter++;
if (eq_sync_counter >= 5) {
eq_sync_counter = 0;
eq_save_dirty_params();
eq_save_dirty_gain();
eq_save_dirty_names();
}
#endif
}
break;
}
}
}
void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol)
{
i2c_master_if i2c[1];

View File

@@ -1264,335 +1264,12 @@ void hid_button_task(chanend_t cc_mic_level, chanend_t c_hidRcvData, chanend_t c
port_disable(p_leds);
hwtimer_free(timer);
}
#define KEY_POLLING_INTERVAL 10000000L // 100ms polling interval
void button_task(chanend_t c_hidSendData, chanend_t cc_mic_level, chanend_t c_uac_vol)
{
hwtimer_t timer = hwtimer_alloc();
static int uac_vol=0, last_uac_vol=0;
#if EQ_EN
// 改动原因添加EQ参数存储计数器用于每500ms保存一次EQ参数
static unsigned eq_sync_counter = 0; // EQ参数存储计数器每100ms递增5次后保存
#endif
port_t p_ctrl_keys = XS1_PORT_8C;
// button_task已移至audiohw.xcXC实现
// void button_task(chanend_t c_hidSendData, chanend_t cc_mic_level, chanend_t c_uac_vol)
uint32_t port_ctrl_keys = 0, curr_ctrl_keys = 0, last_ctrl_keys = 0, keys_changed = 0;
uint32_t port_ctrl_keys2 =0, curr_ctrl_keys2 = 0, last_ctrl_keys2 = 0, keys_changed2 = 0;
int debounce_cnt = 0;
static unsigned isMute = 0;
uint8_t tmp = 0;
unsigned hidData0;
delay_milliseconds(200);
codec_init();
unsigned ex3d_key_verified;
delay_milliseconds(300);
debug_printf("button task start\n");
// 改动原因区分Windows模式和UAC1模式分别从不同的flash文件加载
// Windows模式从 "game_mode" 加载(模式值 0-3
// UAC1模式从 "uac1_mode" 加载(等效模式值 0-2
unsigned char saved_mode;
unsigned char saved_uac1_mode = 0;
unsigned char path[] = "game_mode";
unsigned host_os;
GET_SHARED_GLOBAL(host_os, g_host_os);
saved_mode = load_value(path);
debug_printf("Loaded game_mode from flash: %d\n", saved_mode);
#if defined(UAC1_MODE)
if (saved_mode == 255) {
saved_mode = 0;
save_value(path, saved_mode);
debug_printf("Saved game_mode to flash: %d\n", saved_mode);
}
#else
if (saved_mode == 255) {
// 如果文件不存在,根据编译模式设置默认值
#if defined(STEREO_2K)
saved_mode = 0;
#elif defined(SPATIAL_GAME)
saved_mode = 2;
#elif defined(SPATIAL_MOVIE)
saved_mode = 3;
#endif
save_value(path, saved_mode);
debug_printf("Saved game_mode to flash: %d\n", saved_mode);
}
#endif
printf("host_os: %d\n", host_os);
#if 0
if ((host_os == OS_OTHERS) || (host_os == 0)) {
// 改动原因检测到非Windows系统切换到UAC1模式
#if !UAC1_MODE
printf("Detected non-Windows OS (OS_OTHERS), switching to UAC1 mode\n");
SetRoleSwitchFlag(MODE_UAC1);
debug_printf("Set role switch flag: 0x%04X (UAC1), device will restart\n", MODE_UAC1);
// 设备会重启,退出函数
device_reboot();
while(1);
#endif
}
#endif
#if UAC1_MODE == 1
if (host_os == OS_WIN) {
printf("Detected Windows OS (OS_WIN) saved_mode: %d\n", saved_mode);
#if IR_SWITCHING_MODE
if (saved_mode == 0) {
SetRoleSwitchFlag(MODE_STEREO_2K);
} else {
SetRoleSwitchFlag(MODE_SPATIAL_GAME);
}
#else
{
switch (saved_mode) {
case 0:
SetRoleSwitchFlag(MODE_STEREO_2K);
break;
case 1:
SetRoleSwitchFlag(MODE_STEREO_2K);
break;
case 2:
SetRoleSwitchFlag(MODE_SPATIAL_GAME);
break;
case 3:
SetRoleSwitchFlag(MODE_SPATIAL_MOVIE);
break;
default:
SetRoleSwitchFlag(MODE_STEREO_2K);
break;
}
debug_printf("Switch to saved mode: %d, device will restart.\n", saved_mode);
delay_milliseconds(200);
device_reboot();
while (1);
}
#endif
}
#endif
#if defined(STEREO_2K)
if ((saved_mode != 255) && (saved_mode != 0) && (saved_mode != 1)) {
saved_mode = 0;
}
#endif
#if defined(SPATIAL_GAME)
if ((saved_mode != 255) && (saved_mode != 2)) {
saved_mode = 2;
}
#endif
#if defined(SPATIAL_MOVIE)
if ((saved_mode != 255) && (saved_mode != 3)) {
saved_mode = 3;
}
#endif
g_windows_detect_done = 1;
// 改动原因更新全局变量g_game_mode为保存的模式值
SET_SHARED_GLOBAL(g_game_mode, saved_mode);
debug_printf("Set new g_game_mode to %d\n", saved_mode);
chan_out_byte(cc_mic_level, 0xFC); // 音频模式传输命令
chan_out_byte(cc_mic_level, saved_mode); // 音频模式值0=无音效1=STEREO_2K2=SPATIAL_GAME3=SPATIAL_MOVIE
debug_printf("Sent audio_mode %d to hid_button_task via cc_mic_level channel\n", saved_mode);
port_enable(p_ctrl_keys);
port_ctrl_keys = port_in(p_ctrl_keys);
if ((port_ctrl_keys & KEY_MUTE) == 0) {
// mute switch off (unmuted)
debug_printf("unmuted\n");
isMute = 0;
SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_OFF); // 初始化tile0静音状态
} else {
// mute switch on (muted)
debug_printf("muted\n");
isMute = 1;
SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_ON); // 初始化tile0静音状态
}
last_ctrl_keys = ((port_ctrl_keys) & KEY_BITS);
last_ctrl_keys ^= KEY_MUTE;
hwtimer_set_trigger_time(timer, hwtimer_get_time(timer) + KEY_POLLING_INTERVAL);
while (1) {
SELECT_RES(
CASE_THEN(c_hidSendData, event_hid)
, CASE_THEN(timer, event_polling)
)
{
event_hid:
{
unsigned hidData0 = chan_in_word(c_hidSendData);
unsigned *reportData = hidSendData;
reportData[0] = hidData0;
if (hidData0 == 0xffffffff) {
debug_printf("receive end data\n");
// 改动原因按键短按切换模式时tile1(hid_button_task) 已经完成“算新模式 + 立即切灯/算法”;
// tile0(button_task) 这里只负责接收最终模式值并保存(共享变量 + flash避免再次循环计算/触发重启导致不同步。
unsigned new_mode = chan_in_word(c_hidSendData);
debug_printf("received new_mode from tile1: %d\n", new_mode);
if (new_mode > 3) {
new_mode = 0;
}
// 改动原因把模式值传到tile0保存RAM共享变量用于运行时同步flash用于掉电保持
SET_SHARED_GLOBAL(g_game_mode, new_mode);
unsigned char path[] = "game_mode";
save_value(path, (unsigned char)new_mode);
break;
}
#if (HID_CONTROLS == 1)
for (int i=1; i<(HID_MAX_DATA_BYTES/4); i++) {
reportData[i] = chan_in_word(c_hidSendData);
}
hidSetChangePending(1);
#endif
continue;
}
event_polling:
{
hwtimer_set_trigger_time(timer, hwtimer_get_time(timer) + KEY_POLLING_INTERVAL);
// 改动原因将按键处理逻辑从dnr_dsp_buffer_task合并到button_task统一管理所有按键事件
// 改动原因按键检测间隔从100ms改为10ms提高响应速度每次轮询都检测按键状态
hidData0 = 0;
curr_ctrl_keys = (port_in(p_ctrl_keys)) & KEY_BITS;
// 改动原因:使用防抖机制,连续检测到相同状态变化才响应,避免按键抖动
if (curr_ctrl_keys != last_ctrl_keys) {
debounce_cnt++; // 状态变化计数器递增
if (debounce_cnt >= 2) { // 连续2次检测到相同变化20ms防抖确认按键状态变化
debounce_cnt = 0;
keys_changed = curr_ctrl_keys ^ last_ctrl_keys;
tmp = 0;
switch (keys_changed) {
case KEY_MIC_VOL_DN:
case KEY_MIC_VOL_UP:
// 改动原因:处理麦克风音量按键(仅在非静音状态下处理)
if ((isMute == 0) && ((curr_ctrl_keys & KEY_MIC_VOL_DN) == 0)) {
tmp = KEY_MIC_VOL_DN;
debug_printf("KEY_MIC_VOL_DN pressed\n");
} else {
if ((isMute == 0) && ((curr_ctrl_keys & KEY_MIC_VOL_UP) == 0)) {
tmp = KEY_MIC_VOL_UP;
debug_printf("KEY_MIC_VOL_UP pressed\n");
}
}
if (tmp) {
g_mic_vol_cmd_pending = (unsigned)tmp;
}
break;
case KEY_MUTE:
// 改动原因处理静音按键更新静音状态并通知hid_button_task和dnr_dsp_buffer_task
if ((curr_ctrl_keys & KEY_MUTE) == 0) {
// mute switch off (unmuted)
isMute = 0;
SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_OFF); // 更新tile0静音状态
chan_out_byte(cc_mic_level, (uint8_t) UNMUTED_MIC);
debug_printf("MUTE: unmuted\n");
} else {
// mute switch on (muted)
isMute = 1;
SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_ON); // 更新tile0静音状态
chan_out_byte(cc_mic_level, (uint8_t) MUTED_MIC);
debug_printf("MUTE: muted\n");
}
break;
#if (HID_CONTROLS == 1)
case KEY_PLAY_VOL_DN:
// 改动原因处理播放音量减按键发送HID控制命令
if ((curr_ctrl_keys & KEY_PLAY_VOL_DN) == 0) {
debug_printf("KEY_PLAY_VOL_DN pressed\n");
update_button(HID_CONTROL_VOLDN); // 更新HID报告
}
break;
case KEY_PLAY_VOL_UP:
// 改动原因处理播放音量增按键发送HID控制命令
if ((curr_ctrl_keys & KEY_PLAY_VOL_UP) == 0) {
update_button(HID_CONTROL_VOLUP); // 更新HID报告
}
break;
#endif
default:
break;
}
last_ctrl_keys = curr_ctrl_keys; // 更新上次按键状态
}
} else {
debounce_cnt = 0; // 状态未变化,重置防抖计数器
}
GET_SHARED_GLOBAL(uac_vol, g_uac_vol);
if (uac_vol != last_uac_vol) {
unsigned conv_vol = (0xffffffff - uac_vol + 1) >> 8;
//debug_printf("vol: - %d\n", conv_vol);
chan_out_word(c_uac_vol, conv_vol);
last_uac_vol = uac_vol;
}
#if EQ_EN
// 改动原因EQ模式切换处理每100ms检查一次从AudioHwRemote2整合到button_task
{
unsigned request_eq_mode, new_eq_mode;
unsigned force_request_mode_change;
GET_SHARED_GLOBAL(request_eq_mode, g_request_eq_mode);
GET_SHARED_GLOBAL(new_eq_mode, g_new_eq_mode);
GET_SHARED_GLOBAL(force_request_mode_change, g_force_request_eq_mode_change);
if (new_eq_mode != request_eq_mode)
{
// debug_printf("new eq mode != request eq mode\n");
SET_SHARED_GLOBAL(g_new_eq_mode, request_eq_mode);
}
if (force_request_mode_change == 1)
{
debug_printf("force request mode change\n");
SET_SHARED_GLOBAL(g_force_eq_mode_change, 1);
SET_SHARED_GLOBAL(g_force_request_eq_mode_change, 0);
}
}
// 改动原因EQ参数存储处理每500ms保存一次从AudioHwRemote2整合到button_task
eq_sync_counter++;
if (eq_sync_counter >= 5) { // 5 * 100ms = 500ms
eq_sync_counter = 0;
// 使用单参数存储系统保存脏参数独立保存EQ参数
if (eq_save_dirty_params() == 0) {
//debug_printf("Single EQ parameters synced to Flash successfully\n");
} else {
//debug_printf("Failed to sync single EQ parameters to Flash\n");
}
// 独立保存脏的增益
if (eq_save_dirty_gain() == 0) {
// debug_printf("Gain synced to Flash successfully\n");
} else {
//debug_printf("Failed to sync gain to Flash\n");
}
// 独立保存脏的模式名称
if (eq_save_dirty_names() == 0) {
// debug_printf("Mode names synced to Flash successfully\n");
} else {
//debug_printf("Failed to sync mode names to Flash\n");
}
}
#endif
continue;
}
}
}
// 供audiohw.xcXC调用的字节发送包装函数chan_out_byte的outuchar在XC asm中不可用
void xc_chan_out_byte(unsigned c, unsigned char b) {
chan_out_byte(c, b);
}
chanend_t uc_dsp_to_ex3d[DSP_WORKER_COUNT];