feat:adjust gain in speaker volume. add led and auto-disconnect toggle in mute.
This commit is contained in:
+1
-1
@@ -62,7 +62,7 @@ target_compile_definitions(ds5-bridge PRIVATE
|
|||||||
)
|
)
|
||||||
|
|
||||||
pico_set_program_name(ds5-bridge "ds5-bridge")
|
pico_set_program_name(ds5-bridge "ds5-bridge")
|
||||||
pico_set_program_version(ds5-bridge "0.2")
|
pico_set_program_version(ds5-bridge "0.3")
|
||||||
|
|
||||||
# Modify the below lines to enable/disable output over UART/USB
|
# Modify the below lines to enable/disable output over UART/USB
|
||||||
pico_enable_stdio_uart(ds5-bridge 1)
|
pico_enable_stdio_uart(ds5-bridge 1)
|
||||||
|
|||||||
@@ -10,11 +10,15 @@
|
|||||||
3. 将 DS5 手柄进入蓝牙配对模式
|
3. 将 DS5 手柄进入蓝牙配对模式
|
||||||
4. Enjoy it
|
4. Enjoy it
|
||||||
|
|
||||||
|
- 调整扬声器音量为改变震动增益倍数,范围 [1,2]
|
||||||
|
- 开启扬声器静音为关闭LED连接提示 (手柄重连后生效)
|
||||||
|
- 开启麦克风静音为禁用静默断连
|
||||||
|
|
||||||
# 当前问题:
|
# 当前问题:
|
||||||
当前无,待反馈
|
当前无,待反馈
|
||||||
|
|
||||||
# 未来计划
|
# 未来计划
|
||||||
~~1. 耳机与扬声器支持~~ (pico 性能不够)
|
~~1. 耳机与扬声器支持~~ (pico 性能不够用于opus编码)
|
||||||
|
|
||||||
# 编译
|
# 编译
|
||||||
需要将pico sdk里面的tinyusb版本升级到最新
|
需要将pico sdk里面的tinyusb版本升级到最新
|
||||||
|
|||||||
+4
-3
@@ -6,6 +6,7 @@
|
|||||||
#include "bt.h"
|
#include "bt.h"
|
||||||
#include "resample.h"
|
#include "resample.h"
|
||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
|
#include "usb.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#define INPUT_CHANNELS 4
|
#define INPUT_CHANNELS 4
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
#define SAMPLE_SIZE 64
|
#define SAMPLE_SIZE 64
|
||||||
#define REPORT_SIZE 142
|
#define REPORT_SIZE 142
|
||||||
#define REPORT_ID 0x32
|
#define REPORT_ID 0x32
|
||||||
#define VOLUME_GAIN 2
|
// #define VOLUME_GAIN 2
|
||||||
#define BUFFER_LENGTH 55
|
#define BUFFER_LENGTH 55
|
||||||
|
|
||||||
static WDL_Resampler resampler;
|
static WDL_Resampler resampler;
|
||||||
@@ -49,8 +50,8 @@ void audio_loop() {
|
|||||||
|
|
||||||
// 4. 转换为int8并缓冲,满64字节即组包发送
|
// 4. 转换为int8并缓冲,满64字节即组包发送
|
||||||
for (int i = 0; i < out_frames; i++) {
|
for (int i = 0; i < out_frames; i++) {
|
||||||
int val_l = (int) (out_buf[i * 2] * 127.0f * VOLUME_GAIN);
|
int val_l = (int) (out_buf[i * 2] * 127.0f * (volume[0] ?: 1));
|
||||||
int val_r = (int) (out_buf[i * 2 + 1] * 127.0f * VOLUME_GAIN);
|
int val_r = (int) (out_buf[i * 2 + 1] * 127.0f * (volume[0] ?: 1));
|
||||||
haptic_buf[haptic_buf_pos++] = (int8_t) std::clamp(val_l, -128, 127); // 似乎clamp有点多余?还是以防万一吧
|
haptic_buf[haptic_buf_pos++] = (int8_t) std::clamp(val_l, -128, 127); // 似乎clamp有点多余?还是以防万一吧
|
||||||
haptic_buf[haptic_buf_pos++] = (int8_t) std::clamp(val_r, -128, 127);
|
haptic_buf[haptic_buf_pos++] = (int8_t) std::clamp(val_r, -128, 127);
|
||||||
|
|
||||||
|
|||||||
+17
-12
@@ -15,6 +15,7 @@
|
|||||||
#include "l2cap.h"
|
#include "l2cap.h"
|
||||||
#include "pico/cyw43_arch.h"
|
#include "pico/cyw43_arch.h"
|
||||||
#include "pico/stdio.h"
|
#include "pico/stdio.h"
|
||||||
|
#include "usb.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "bsp/board_api.h"
|
#include "bsp/board_api.h"
|
||||||
#include "pico/sync.h"
|
#include "pico/sync.h"
|
||||||
@@ -28,7 +29,7 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
static btstack_packet_callback_registration_t hci_event_callback_registration, l2cap_event_callback_registration;
|
static btstack_packet_callback_registration_t hci_event_callback_registration, l2cap_event_callback_registration;
|
||||||
static bd_addr_t current_device_addr;
|
static bd_addr_t current_device_addr;
|
||||||
static bool device_found = false;
|
static bool device_found = false;
|
||||||
static bool new_pair = false;
|
static bool new_pair = false; // 只有新匹配的设备才用创建channel,自动重连走的是service
|
||||||
static hci_con_handle_t acl_handle = HCI_CON_HANDLE_INVALID;
|
static hci_con_handle_t acl_handle = HCI_CON_HANDLE_INVALID;
|
||||||
static uint16_t hid_control_cid;
|
static uint16_t hid_control_cid;
|
||||||
static uint16_t hid_interrupt_cid;
|
static uint16_t hid_interrupt_cid;
|
||||||
@@ -302,6 +303,7 @@ static void hci_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *p
|
|||||||
hid_control_cid = 0;
|
hid_control_cid = 0;
|
||||||
hid_interrupt_cid = 0;
|
hid_interrupt_cid = 0;
|
||||||
feature_data.clear();
|
feature_data.clear();
|
||||||
|
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, false);
|
||||||
printf("[HCI] Disconnected reason=0x%02X, start inquiry\n", reason);
|
printf("[HCI] Disconnected reason=0x%02X, start inquiry\n", reason);
|
||||||
gap_inquiry_start(30);
|
gap_inquiry_start(30);
|
||||||
break;
|
break;
|
||||||
@@ -317,7 +319,9 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
// printf("[L2CAP] HID Interrupt data len=%u\n", size);
|
// printf("[L2CAP] HID Interrupt data len=%u\n", size);
|
||||||
// printf_hexdump(packet, size);
|
// printf_hexdump(packet, size);
|
||||||
bt_data_callback(INTERRUPT, packet, size);
|
bt_data_callback(INTERRUPT, packet, size);
|
||||||
if (packet[3] < 120 || packet[3] > 140) {
|
|
||||||
|
// 静默检测
|
||||||
|
if (!mute[1] && (packet[3] < 120 || packet[3] > 140)) {
|
||||||
inactive_time = time_us_32();
|
inactive_time = time_us_32();
|
||||||
}else if (time_us_32() - inactive_time > 600 * 1000 * 1000){
|
}else if (time_us_32() - inactive_time > 600 * 1000 * 1000){
|
||||||
printf("disconnect when inactive\n");
|
printf("disconnect when inactive\n");
|
||||||
@@ -354,33 +358,34 @@ static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t
|
|||||||
printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", local_cid);
|
printf("[L2CAP] HID Interrupt opened cid=0x%04X\n", local_cid);
|
||||||
hid_interrupt_cid = local_cid;
|
hid_interrupt_cid = local_cid;
|
||||||
|
|
||||||
printf("Init DualSense\n");
|
if (!mute[0]) {
|
||||||
uint8_t get_feature[41] = {
|
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true);
|
||||||
0x43,
|
}
|
||||||
0x05
|
|
||||||
};
|
|
||||||
l2cap_send(hid_control_cid, get_feature, 41);
|
|
||||||
|
|
||||||
|
printf("Init DualSense\n");
|
||||||
|
|
||||||
|
init_feature();
|
||||||
|
// 初始化手柄状态
|
||||||
uint8_t report32[142];
|
uint8_t report32[142];
|
||||||
report32[0] = 0x32;
|
report32[0] = 0x32;
|
||||||
report32[1] = 0x10;
|
report32[1] = 0x10; // reportSeqCounter
|
||||||
uint8_t packet_0x10[] =
|
uint8_t packet_0x10[] =
|
||||||
{
|
{
|
||||||
0x90, // Packet: 0x10
|
0x90, // Packet: 0x10
|
||||||
0x3f, // 63
|
0x3f, // 63
|
||||||
// SetStateData
|
// SetStateData
|
||||||
0xfd, 0xf7, 0x0, 0x0, 0x7f, 0x7f,
|
0xfd, 0xf7, 0x0, 0x0,
|
||||||
|
0x7f, 0x7f, // Headphones, Speaker
|
||||||
0xff, 0x9, 0x0, 0xf, 0x0, 0x0, 0x0, 0x0,
|
0xff, 0x9, 0x0, 0xf, 0x0, 0x0, 0x0, 0x0,
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa,
|
||||||
0x7, 0x0, 0x0, 0x2, 0x1,
|
0x7, 0x0, 0x0, 0x2, 0x1,
|
||||||
0x00,
|
0x00,
|
||||||
0xff, 0xd7, 0x00 // RGB LED: R, G, B
|
0xff, 0xd7, 0x00 // RGB LED: R, G, B (Nijika Color!)✨
|
||||||
};
|
};
|
||||||
memcpy(report32 + 2, packet_0x10, sizeof(packet_0x10));
|
memcpy(report32 + 2, packet_0x10, sizeof(packet_0x10));
|
||||||
bt_write(report32, sizeof(report32));
|
bt_write(report32, sizeof(report32));
|
||||||
init_feature();
|
|
||||||
} else {
|
} else {
|
||||||
printf("[L2CAP] Unknown Channel psm: 0x%02X", psm);
|
printf("[L2CAP] Unknown Channel psm: 0x%02X", psm);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
int reportSeqCounter = 0;
|
int reportSeqCounter = 0;
|
||||||
uint8_t packetCounter = 0;
|
uint8_t packetCounter = 0;
|
||||||
uint32_t lastTime = 0;
|
|
||||||
|
|
||||||
uint8_t interrupt_in_data[63] = {
|
uint8_t interrupt_in_data[63] = {
|
||||||
0x7f, 0x7d, 0x7f, 0x7e, 0x00, 0x00, 0xa7,
|
0x7f, 0x7d, 0x7f, 0x7e, 0x00, 0x00, 0xa7,
|
||||||
|
|||||||
+20
-19
@@ -5,8 +5,9 @@
|
|||||||
#include "tusb.h"
|
#include "tusb.h"
|
||||||
#include "bsp/board_api.h"
|
#include "bsp/board_api.h"
|
||||||
|
|
||||||
uint8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
uint8_t mute[2]; // 0: SPEAKER(0x02) 1: MIC(0x05)
|
||||||
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
|
int16_t volume[2]; // 0: SPEAKER(0x02) 1: MIC(0x05)
|
||||||
|
|
||||||
#define UAC1_ENTITY_SPK_FEATURE_UNIT 0x02
|
#define UAC1_ENTITY_SPK_FEATURE_UNIT 0x02
|
||||||
#define UAC1_ENTITY_MIC_FEATURE_UNIT 0x05
|
#define UAC1_ENTITY_MIC_FEATURE_UNIT 0x05
|
||||||
|
|
||||||
@@ -38,9 +39,10 @@ static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint
|
|||||||
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
|
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
|
||||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||||
|
uint8_t index = entityID == UAC1_ENTITY_SPK_FEATURE_UNIT ? 0 : 1;
|
||||||
|
|
||||||
// If request is for our speaker feature unit
|
// If request is for our speaker feature unit
|
||||||
if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT) {
|
if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT || entityID == UAC1_ENTITY_MIC_FEATURE_UNIT) {
|
||||||
switch (ctrlSel) {
|
switch (ctrlSel) {
|
||||||
case AUDIO10_FU_CTRL_MUTE:
|
case AUDIO10_FU_CTRL_MUTE:
|
||||||
switch (p_request->bRequest) {
|
switch (p_request->bRequest) {
|
||||||
@@ -48,9 +50,9 @@ static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint
|
|||||||
// Only 1st form is supported
|
// Only 1st form is supported
|
||||||
TU_VERIFY(p_request->wLength == 1);
|
TU_VERIFY(p_request->wLength == 1);
|
||||||
|
|
||||||
mute[channelNum] = pBuff[0];
|
mute[index] = pBuff[0];
|
||||||
|
|
||||||
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
|
TU_LOG2(" Set Mute: %d of entity: %u\r\n", mute[index], entityID);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -63,9 +65,9 @@ static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint
|
|||||||
// Only 1st form is supported
|
// Only 1st form is supported
|
||||||
TU_VERIFY(p_request->wLength == 2);
|
TU_VERIFY(p_request->wLength == 2);
|
||||||
|
|
||||||
volume[channelNum] = (int16_t) tu_unaligned_read16(pBuff) / 256;
|
volume[index] = (int16_t) tu_unaligned_read16(pBuff) / 256;
|
||||||
|
|
||||||
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
|
TU_LOG2(" Set Volume: %d dB of entity: %u\r\n", volume[index], entityID);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -86,7 +88,7 @@ static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const
|
|||||||
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
|
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
|
||||||
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
|
||||||
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
|
||||||
// 当前音量的调节不会影响音量大小,看看后面有没有用再补全吧
|
uint8_t index = entityID == UAC1_ENTITY_SPK_FEATURE_UNIT ? 0 : 1;
|
||||||
|
|
||||||
// If request is for our speaker feature unit
|
// If request is for our speaker feature unit
|
||||||
if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT || entityID == UAC1_ENTITY_MIC_FEATURE_UNIT) {
|
if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT || entityID == UAC1_ENTITY_MIC_FEATURE_UNIT) {
|
||||||
@@ -94,36 +96,35 @@ static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const
|
|||||||
case AUDIO10_FU_CTRL_MUTE:
|
case AUDIO10_FU_CTRL_MUTE:
|
||||||
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
|
||||||
// There does not exist a range parameter block for mute
|
// There does not exist a range parameter block for mute
|
||||||
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
|
TU_LOG2(" Get Mute of entity: %u\r\n", entityID);
|
||||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
|
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[index], 1);
|
||||||
|
|
||||||
case AUDIO10_FU_CTRL_VOLUME:
|
case AUDIO10_FU_CTRL_VOLUME:
|
||||||
switch (p_request->bRequest) {
|
switch (p_request->bRequest) {
|
||||||
case AUDIO10_CS_REQ_GET_CUR:
|
case AUDIO10_CS_REQ_GET_CUR:
|
||||||
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum); {
|
TU_LOG2(" Get Volume of entity: %u\r\n", entityID); {
|
||||||
int16_t vol = (int16_t) volume[channelNum];
|
int16_t vol = volume[index];
|
||||||
vol = vol * 256; // convert to 1/256 dB units
|
vol = vol * 256; // convert to 1/256 dB units
|
||||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &vol, sizeof(vol));
|
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &vol, sizeof(vol));
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUDIO10_CS_REQ_GET_MIN:
|
case AUDIO10_CS_REQ_GET_MIN:
|
||||||
TU_LOG2(" Get Volume min of channel: %u\r\n", channelNum); {
|
TU_LOG2(" Get Volume min of entity: %u\r\n", entityID); {
|
||||||
int16_t min = -100; // -100 dB
|
int16_t min = 1; // 1 dB
|
||||||
min = min * 256; // convert to 1/256 dB units
|
min = min * 256; // convert to 1/256 dB units
|
||||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &min, sizeof(min));
|
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &min, sizeof(min));
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUDIO10_CS_REQ_GET_MAX:
|
case AUDIO10_CS_REQ_GET_MAX:
|
||||||
TU_LOG2(" Get Volume max of channel: %u\r\n", channelNum); {
|
TU_LOG2(" Get Volume max of entity: %u\r\n", entityID); {
|
||||||
int16_t max = 0; // 0 dB
|
int16_t max = 2; // 2 dB
|
||||||
max = max * 256; // convert to 1/256 dB units
|
max = max * 256; // convert to 1/256 dB units
|
||||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &max, sizeof(max));
|
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &max, sizeof(max));
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUDIO10_CS_REQ_GET_RES:
|
case AUDIO10_CS_REQ_GET_RES:
|
||||||
TU_LOG2(" Get Volume res of channel: %u\r\n", channelNum); {
|
TU_LOG2(" Get Volume res of entity: %u\r\n", entityID); {
|
||||||
int16_t res = 1; // 1 dB
|
int16_t res = 0.1 * 256; // 0.1 dB
|
||||||
res = res * 256; // convert to 1/256 dB units
|
|
||||||
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &res, sizeof(res));
|
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &res, sizeof(res));
|
||||||
}
|
}
|
||||||
// Unknown/Unsupported control
|
// Unknown/Unsupported control
|
||||||
|
|||||||
@@ -5,4 +5,7 @@
|
|||||||
#ifndef DS5_BRIDGE_USB_H
|
#ifndef DS5_BRIDGE_USB_H
|
||||||
#define DS5_BRIDGE_USB_H
|
#define DS5_BRIDGE_USB_H
|
||||||
|
|
||||||
|
extern uint8_t mute[2]; // 0: SPEAKER(0x02) 1: MIC(0x05)
|
||||||
|
extern int16_t volume[2]; // 0: SPEAKER(0x02) 1: MIC(0x05)
|
||||||
|
|
||||||
#endif //DS5_BRIDGE_USB_H
|
#endif //DS5_BRIDGE_USB_H
|
||||||
Reference in New Issue
Block a user