nrf5340 audio - gateway playback - bad sound quality

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

Parents Reply Children
No Data
Related