Hi,
I am trying to get playback on gateway device so it can act as an additional speaker. I configured hw codec and tried to redirect data directly from USB to hw codec, but something is not working as expected.
I got the gateway to play sound received over USB from PC. However sound quality is quite bad. I was wondering if maybe hw codec configuration is invalid, but I think I applied the same configuration as in Headset mode.This makes me think there is something wrong in the way I copy data from USB handler to audio_datapath fifo. I wonder if maybe bitrate is different for USB stream vs what is expected by hw codec ?
Here is modified USB handler:
static uint16_t audio_data[960]; static uint16_t buf_count = 0; static void data_received(const struct device *dev, struct net_buf *buffer, size_t size) { int ret; void *data_in; if (fifo_rx == NULL) { /* Throwing away data */ net_buf_unref(buffer); return; } if (!buffer || !size) { /* This should never happen */ ERR_CHK(-EINVAL); } /* Receive data from USB */ if (size != USB_FRAME_SIZE_STEREO) { LOG_WRN("Wrong length: %d", size); net_buf_unref(buffer); return; } // Added code LOG_INF("Size %d, net len %d", size, buffer->len); static uint32_t recv_frame_ts_us; if (buf_count == 0) { recv_frame_ts_us = audio_sync_timer_curr_time_get(); } memcpy(&audio_data[96 * buf_count], buffer->data, 192); buf_count++; if (buf_count >= 10) { LOG_INF("Sending buffer %d", buf_count * 96 * 2); audio_datapath_add(audio_data, recv_frame_ts_us); buf_count = 0; } // Added code end ret = data_fifo_pointer_first_vacant_get(fifo_rx, &data_in, K_NO_WAIT); /* RX FIFO can fill up due to retransmissions or disconnect */ if (ret == -ENOMEM) { void *temp; size_t temp_size; LOG_WRN("USB RX overrun"); ret = data_fifo_pointer_last_filled_get(fifo_rx, &temp, &temp_size, K_NO_WAIT); ERR_CHK(ret); data_fifo_block_free(fifo_rx, &temp); ret = data_fifo_pointer_first_vacant_get(fifo_rx, &data_in, K_NO_WAIT); } ERR_CHK_MSG(ret, "RX failed to get block"); memcpy(data_in, buffer->data, size); ret = data_fifo_block_lock(fifo_rx, &data_in, size); ERR_CHK_MSG(ret, "Failed to lock block"); net_buf_unref(buffer); }
Here is new audio_datapath_add function that I am calling - it is doing the same as audio_datapath_stream_out but without decoding part that is not needed here:
void audio_datapath_add(uint16_t * p_data, uint32_t recv_frame_ts_us) { /*** Add audio data to FIFO buffer ***/ int32_t num_blks_in_fifo = ctrl_blk.out.prod_blk_idx - ctrl_blk.out.cons_blk_idx; if ((num_blks_in_fifo + NUM_BLKS_IN_FRAME) > FIFO_NUM_BLKS) { LOG_WRN("Output audio stream overrun - Discarding audio frame"); /* Discard frame to allow consumer to catch up */ return; } uint32_t out_blk_idx = ctrl_blk.out.prod_blk_idx; LOG_INF("EXPECTED %d * %d, offset = %d", NUM_BLKS_IN_FRAME, BLK_STEREO_SIZE_OCTETS, BLK_STEREO_NUM_SAMPS); for (uint32_t i = 0; i < NUM_BLKS_IN_FRAME; i++) { memcpy(&ctrl_blk.out.fifo[out_blk_idx * BLK_STEREO_NUM_SAMPS], &p_data[i * BLK_STEREO_NUM_SAMPS], BLK_STEREO_SIZE_OCTETS); /* Record producer block start reference */ ctrl_blk.out.prod_blk_ts[out_blk_idx] = recv_frame_ts_us + (i * BLK_PERIOD_US); out_blk_idx = NEXT_IDX(out_blk_idx); } ctrl_blk.out.prod_blk_idx = out_blk_idx; }
Last is blk_complete function. I believe I only changed conditions to make gateway send data over i2s:
static void audio_datapath_i2s_blk_complete(uint32_t frame_start_ts, uint32_t *rx_buf_released, uint32_t const *tx_buf_released) { int ret; static bool underrun_condition; alt_buffer_free(tx_buf_released); /*** Presentation delay measurement ***/ ctrl_blk.current_pres_dly_us = frame_start_ts - ctrl_blk.out.prod_blk_ts[ctrl_blk.out.cons_blk_idx]; /********** I2S TX **********/ static uint8_t *tx_buf; if (true){ // IS_ENABLED(CONFIG_STREAM_BIDIRECTIONAL) || (config_audio_dev_var == HEADSET)) { if (tx_buf_released != NULL) { /* Double buffered index */ uint32_t next_out_blk_idx = NEXT_IDX(ctrl_blk.out.cons_blk_idx); if (next_out_blk_idx != ctrl_blk.out.prod_blk_idx) { /* Only increment if not in underrun condition */ ctrl_blk.out.cons_blk_idx = next_out_blk_idx; if (underrun_condition) { underrun_condition = false; LOG_WRN("Data received, total underruns: %d", ctrl_blk.out.total_blk_underruns); } tx_buf = (uint8_t *)&ctrl_blk.out .fifo[next_out_blk_idx * BLK_MONO_SIZE_OCTETS]; } else { if (stream_state_get() == STATE_STREAMING) { underrun_condition = true; ctrl_blk.out.total_blk_underruns++; if ((ctrl_blk.out.total_blk_underruns % UNDERRUN_LOG_INTERVAL_BLKS) == 0) { LOG_WRN("In I2S TX underrun condition, total: %d", ctrl_blk.out.total_blk_underruns); } } /* * No data available in out.fifo * use alternative buffers */ ret = alt_buffer_get((void **)&tx_buf); ERR_CHK(ret); memset(tx_buf, 0, BLK_STEREO_SIZE_OCTETS); } if (tone_active) { tone_mix(tx_buf); } } } /********** I2S RX **********/ static uint32_t *rx_buf; static int prev_ret; if (false) {//IS_ENABLED(CONFIG_STREAM_BIDIRECTIONAL) || (config_audio_dev_var == GATEWAY)) { /* Lock last filled buffer into message queue */ if (rx_buf_released != NULL) { ret = data_fifo_block_lock(ctrl_blk.in.fifo, (void **)&rx_buf_released, BLOCK_SIZE_BYTES); ERR_CHK_MSG(ret, "Unable to lock block RX"); } /* Get new empty buffer to send to I2S HW */ ret = data_fifo_pointer_first_vacant_get(ctrl_blk.in.fifo, (void **)&rx_buf, K_NO_WAIT); if (ret == 0 && prev_ret == -ENOMEM) { LOG_WRN("I2S RX continuing stream"); prev_ret = ret; } /* If RX FIFO is filled up */ if (ret == -ENOMEM) { void *data; size_t size; if (ret != prev_ret) { LOG_WRN("I2S RX overrun. Single msg"); prev_ret = ret; } ret = data_fifo_pointer_last_filled_get(ctrl_blk.in.fifo, &data, &size, K_NO_WAIT); ERR_CHK(ret); data_fifo_block_free(ctrl_blk.in.fifo, &data); ret = data_fifo_pointer_first_vacant_get(ctrl_blk.in.fifo, (void **)&rx_buf, K_NO_WAIT); } ERR_CHK_MSG(ret, "RX failed to get block"); } /*** Data exchange ***/ audio_i2s_set_next_buf(tx_buf, rx_buf); /*** Drift compensation ***/ audio_datapath_drift_compensation(frame_start_ts); }
I wonder what am I doing wrong. Also interested in your opinion, if I selected correct "anchors" to redirect data from USB to output. Can it be done simpler/ more elegant way?
Best regards.
Michal