mirror of
https://github.com/google/pebble.git
synced 2025-06-01 16:03:11 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
183
src/fw/drivers/mic/mic_command.c
Normal file
183
src/fw/drivers/mic/mic_command.c
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "board/board.h"
|
||||
#include "drivers/mic.h"
|
||||
#include "drivers/accessory.h"
|
||||
#include "kernel/events.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#if RECOVERY_FW
|
||||
#include "services/prf/accessory/accessory_manager.h"
|
||||
#else
|
||||
#include "services/normal/accessory/accessory_manager.h"
|
||||
#endif
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/circular_buffer.h"
|
||||
#include "util/legacy_checksum.h"
|
||||
#include "system/profiler.h"
|
||||
|
||||
static int s_timeout = 0;
|
||||
static bool s_is_8_bit;
|
||||
static bool s_is_8khz;
|
||||
static TimerID s_start_timer;
|
||||
static int16_t *s_test_buffer;
|
||||
|
||||
static void prv_put_byte(uint8_t datum) {
|
||||
accessory_send_data(&datum, 1);
|
||||
}
|
||||
|
||||
static const uint8_t HDLC_START = 0x7E;
|
||||
static const uint8_t HDLC_ESCAPE = 0x7D;
|
||||
static const uint8_t HDLC_ESCAPE_MASK = 0x20;
|
||||
|
||||
static void prv_put_hdlc_frame_delimiter(void) {
|
||||
prv_put_byte(HDLC_START);
|
||||
}
|
||||
|
||||
static void prv_put_byte_hdlc(uint8_t datum) {
|
||||
if ((datum == HDLC_ESCAPE) || (datum == HDLC_START)) {
|
||||
prv_put_byte(HDLC_ESCAPE);
|
||||
prv_put_byte(datum ^ HDLC_ESCAPE_MASK);
|
||||
} else {
|
||||
prv_put_byte(datum);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_prompt_output_cb(int16_t *samples, size_t sample_count, void *context) {
|
||||
const int OUTPUT_SAMPLE_RATE = (s_is_8khz) ? 8000 : 16000;
|
||||
|
||||
int to_process = MIN((int)sample_count, s_timeout);
|
||||
s_timeout -= to_process;
|
||||
|
||||
if (to_process > 0) {
|
||||
// Groups of samples are encapsulated in HDLC-like framing in order to verify the integrity of
|
||||
// samples
|
||||
prv_put_hdlc_frame_delimiter();
|
||||
|
||||
int num_bytes = (to_process * (s_is_8_bit ? 1 : 2)) / (MIC_SAMPLE_RATE/OUTPUT_SAMPLE_RATE);
|
||||
|
||||
// Store frame in temporary buffer in order to calculate CRC after subsample and/or bit-width
|
||||
// conversion
|
||||
uint8_t buf[num_bytes];
|
||||
int buf_idx = 0;
|
||||
|
||||
uint8_t *d = (uint8_t *)samples;
|
||||
// Subsample by skipping every second sample if output rate is set to 8kHz
|
||||
for (int i = 0; i < to_process * 2; i += 2 * (MIC_SAMPLE_RATE/OUTPUT_SAMPLE_RATE)) {
|
||||
if (s_is_8_bit) {
|
||||
// Convert 16-bit PCM representation to 8-bit PCM representation
|
||||
uint8_t b = d[i + 1] ^ 0x80;
|
||||
buf[buf_idx++] = b;
|
||||
prv_put_byte_hdlc(b);
|
||||
} else {
|
||||
buf[buf_idx++] = d[i];
|
||||
prv_put_byte_hdlc(d[i]);
|
||||
buf[buf_idx++] = d[i + 1];
|
||||
prv_put_byte_hdlc(d[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t crc = legacy_defective_checksum_memory(buf, num_bytes);
|
||||
prv_put_byte_hdlc((uint8_t) crc);
|
||||
prv_put_byte_hdlc((uint8_t) (crc >> 8));
|
||||
prv_put_byte_hdlc((uint8_t) (crc >> 16));
|
||||
prv_put_byte_hdlc((uint8_t) (crc >> 24));
|
||||
|
||||
prv_put_hdlc_frame_delimiter();
|
||||
}
|
||||
|
||||
if (s_timeout == 0) {
|
||||
mic_stop(MIC);
|
||||
PROFILER_STOP;
|
||||
PROFILER_PRINT_STATS;
|
||||
|
||||
kernel_free(s_test_buffer);
|
||||
|
||||
accessory_enable_input();
|
||||
#if RECOVERY_FW
|
||||
const AccessoryInputState input_state = AccessoryInputStateMfg;
|
||||
#else
|
||||
const AccessoryInputState input_state = AccessoryInputStateIdle;
|
||||
#endif
|
||||
PBL_ASSERTN(accessory_manager_set_state(input_state));
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_mic_start(void *data) {
|
||||
const size_t BUFFER_SIZE = 24;
|
||||
|
||||
new_timer_delete(s_start_timer);
|
||||
|
||||
s_test_buffer = kernel_malloc(BUFFER_SIZE * sizeof(int16_t));
|
||||
|
||||
if (!s_test_buffer) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Failed to malloc buffer for 'mic start' command");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t width = s_is_8_bit ? 8 : 16;
|
||||
uint32_t rate = s_is_8khz ? 8 : 16;
|
||||
PBL_LOG(LOG_LEVEL_ALWAYS, "Starting mic recording: %"PRIu32"-bit @ %"PRIu32"kHz for %"PRIu32
|
||||
" samples", width, rate, (s_timeout / (MIC_SAMPLE_RATE / (rate * 1000))));
|
||||
|
||||
PROFILER_INIT;
|
||||
PROFILER_START;
|
||||
if (!mic_start(MIC, &prv_prompt_output_cb, NULL, s_test_buffer, BUFFER_SIZE)) {
|
||||
kernel_free(s_test_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void command_mic_start(char *timeout_str, char *sample_size_str, char *sample_rate_str,
|
||||
char *volume_str) {
|
||||
static const int MAX_TIMEOUT = 60;
|
||||
|
||||
if (!accessory_manager_set_state(AccessoryInputStateMic)) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "The accessory is already in use!");
|
||||
return;
|
||||
}
|
||||
|
||||
s_timeout = strtol(timeout_str, NULL, 10);
|
||||
if (s_timeout <= 0) {
|
||||
s_timeout = 1;
|
||||
} else if (s_timeout > MAX_TIMEOUT) {
|
||||
s_timeout = MAX_TIMEOUT;
|
||||
}
|
||||
int volume = strtol(volume_str, NULL, 10);
|
||||
mic_set_volume(MIC, MIN(volume, 1024));
|
||||
|
||||
s_is_8_bit = strtol(sample_size_str, NULL, 10) == 8; // assume 16 bit if not 8 bit
|
||||
s_is_8khz = strtol(sample_rate_str, NULL, 10) == 8000; // assume 16kHz if not set to 8000
|
||||
|
||||
// Convert timeout in seconds to sample count so that the exact amount of samples are sent
|
||||
s_timeout *= MIC_SAMPLE_RATE;
|
||||
|
||||
// Boost the accessory connector baud rate if necessary
|
||||
accessory_disable_input();
|
||||
if ((!s_is_8_bit) && (!s_is_8khz)) {
|
||||
accessory_set_baudrate(AccessoryBaud460800);
|
||||
} else if ((!s_is_8_bit) || (!s_is_8khz)) {
|
||||
accessory_set_baudrate(AccessoryBaud230400);
|
||||
}
|
||||
|
||||
// Start timer after a short delay to allow receiving end to switch baud rate
|
||||
s_start_timer = new_timer_create();
|
||||
new_timer_start(s_start_timer, 500, &prv_mic_start, NULL, 0);
|
||||
}
|
||||
|
||||
void command_mic_read() {
|
||||
command_mic_start("3", "16", "16000", "100");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue