Prechádzať zdrojové kódy

Robotic output on DAC

First stab at DAC out.  Right now, some robotic music is comming out
Sebastien Leclerc 5 rokov pred
rodič
commit
8f477fd05e
6 zmenil súbory, kde vykonal 240 pridanie a 23 odobranie
  1. 14 2
      main/esp32.c
  2. 3 1
      main/main.c
  3. 5 5
      main/output_bt.c
  4. 42 13
      main/output_dac.c
  5. 163 0
      main/output_dac.c.tes
  6. 13 2
      main/squeezelite.h

+ 14 - 2
main/esp32.c

@@ -35,7 +35,18 @@ extern struct buffer *streambuf;
 #define LOCK   mutex_lock(outputbuf->mutex)
 #define UNLOCK mutex_unlock(outputbuf->mutex)
 int64_t connecting_timeout = 0;
-
+#ifndef CONFIG_A2DP_SINK_NAME
+#define CONFIG_A2DP_SINK_NAME "btspeaker" // fix some compile errors when BT is not chosen
+#endif
+#ifndef CONFIG_A2DP_CONNECT_TIMEOUT_MS
+#define CONFIG_A2DP_CONNECT_TIMEOUT_MS 2000
+#endif
+#ifndef CONFIG_A2DP_DEV_NAME
+#define CONFIG_A2DP_DEV_NAME "espsqueezelite"
+#endif
+#ifndef CONFIG_A2DP_CONTROL_DELAY_MS
+#define CONFIG_A2DP_CONTROL_DELAY_MS 1000
+#endif
 #define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000)
 #define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout
 
@@ -551,6 +562,7 @@ static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len)
 	return frames * BYTES_PER_FRAME;
 }
 static bool running_test;
+#ifdef BTAUDIO
 bool test_open(const char *device, unsigned rates[], bool userdef_rates) {
 
 //	running_test  = true;
@@ -570,7 +582,7 @@ bool test_open(const char *device, unsigned rates[], bool userdef_rates) {
 	}
 	return true;
 }
-
+#endif
 static void a2d_app_heart_beat(void *arg)
 {
     bt_app_work_dispatch(bt_app_av_sm_hdlr, BT_APP_HEART_BEAT_EVT, NULL, 0, NULL);

+ 3 - 1
main/main.c

@@ -749,7 +749,9 @@ int main(int argc, char **argv) {
 
 	stream_init(log_stream, stream_buf_size);
 
-#if DACAUDIO
+#if BTAUDIO
+	output_init_bt(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
+#elif DACAUDIO
 	output_init_dac(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
 #else
 	if (!strcmp(output_device, "-")) {

+ 5 - 5
main/output_bt.c

@@ -17,10 +17,10 @@ extern struct buffer *streambuf;
 
 extern u8_t *silencebuf;
 extern u8_t *bt_optr;
-
+void hal_bluetooth_init(log_level);
 static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR,
 								s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr);
-
+#if BTAUDIO
 void set_volume(unsigned left, unsigned right) {
 	LOG_DEBUG("setting internal gain left: %u right: %u", left, right);
 	LOCK;
@@ -31,10 +31,10 @@ void set_volume(unsigned left, unsigned right) {
 	output.gainR = FIXED_ONE;
 	UNLOCK;
 }
+#endif
 
 
-
-void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
+void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
 	loglevel = level;
 
 	LOG_INFO("init output BT");
@@ -59,7 +59,7 @@ void output_init_dac(log_level level, char *device, unsigned output_buf_size, ch
 
 }
 
-void output_close_dac(void) {
+void output_close_bt(void) {
 	LOG_INFO("close output");
 	LOCK;
 	running = false;

+ 42 - 13
main/output_dac.c.sample → main/output_dac.c

@@ -1,8 +1,15 @@
 
 #include "squeezelite.h"
-
+#include "driver/i2s.h"
 #include <signal.h>
-
+#define I2S_NUM         (0)
+#define I2S_BCK_IO      (GPIO_NUM_26)
+#define I2S_WS_IO       (GPIO_NUM_25)
+#define I2S_DO_IO       (GPIO_NUM_22)
+#define I2S_DI_IO       (-1)
+
+// buffer length is expressed in number of samples
+#define I2S_BUF_LEN		60
 static log_level loglevel;
 
 static bool running = true;
@@ -39,7 +46,11 @@ void set_volume(unsigned left, unsigned right) {
 
 void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
 	loglevel = level;
-
+	optr = malloc(FRAME_BLOCK * BYTES_PER_FRAME);
+	if (!optr) {
+		LOG_ERROR("unable to malloc buf");
+		return;
+	}
 	LOG_INFO("init output DAC");
 	
 	memset(&output, 0, sizeof(output));
@@ -65,6 +76,25 @@ void output_init_dac(log_level level, char *device, unsigned output_buf_size, ch
 	}
 
 	output_init_common(level, device, output_buf_size, rates, idle);
+	
+	i2s_config_t i2s_config = {
+			.mode = I2S_MODE_MASTER | I2S_MODE_TX,                    // Only TX
+			.sample_rate = output.current_sample_rate,
+			.bits_per_sample = BYTES_PER_FRAME * 8,
+			.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,           //2-channels
+			.communication_format = I2S_COMM_FORMAT_I2S
+					| I2S_COMM_FORMAT_I2S_MSB,
+			.dma_buf_count = 6, //todo: tune this parameter. Expressed in numbrer of buffers
+			.dma_buf_len = I2S_BUF_LEN, // todo: tune this parameter. Expressed in number of samples. Byte size depends on bit depth
+			.use_apll = false,
+			.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1
+			};
+	i2s_pin_config_t pin_config = { .bck_io_num = I2S_BCK_IO, .ws_io_num =
+			I2S_WS_IO, .data_out_num = I2S_DO_IO, .data_in_num = I2S_DI_IO //Not used
+			};
+	i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
+	i2s_set_pin(I2S_NUM, &pin_config);
+	i2s_set_clk(I2S_NUM, output.current_sample_rate, i2s_config.bits_per_sample, 2);
 
 #if LINUX || OSX || FREEBSD || POSIX
 	pthread_attr_t attr;
@@ -86,7 +116,7 @@ void output_close_dac(void) {
 	LOCK;
 	running = false;
 	UNLOCK;
-
+	free(optr);
 	output_close_common();
 }
 
@@ -144,7 +174,7 @@ static void *output_thread() {
 	// buffer to hold output data so we can block on writing outside of output lock, allocated on init
 	u8_t *obuf = malloc(FRAME_BLOCK * BYTES_PER_FRAME);
 	int frames = 0;
-
+	size_t i2s_bytes_write = 0;
 #if REPACK
 	LOCK;
 
@@ -174,7 +204,8 @@ static void *output_thread() {
 			usleep(500000);
 			continue;
 		}		
-			
+		// todo: call i2s_set_clock here if rate is changed
+
 		output.device_frames = 0;
 		output.updated = gettime_ms();
 		output.frames_played_dmp = output.frames_played;
@@ -185,13 +216,11 @@ static void *output_thread() {
 		UNLOCK;
 
 		if (frames) {
-			if (output.device[0] == '-' && memcmp(optr, silencebuf, frames * bytes_per_frame)) {
-				fwrite(obuf, bytes_per_frame, frames, stdout);
-				LOG_INFO("writing frames %d", frames);
-			} else {	
-				// do something with some of these frames...
-				usleep((frames * 1000 * 1000) / output.current_sample_rate);			
-			}	
+			 i2s_write(I2S_NUM, optr,frames*BYTES_PER_FRAME, &i2s_bytes_write, 100);
+			if(i2s_bytes_write!=frames*BYTES_PER_FRAME){
+				LOG_WARN("Bytes available: %d, I2S wrote %d", frames*BYTES_PER_FRAME,i2s_bytes_write);
+			}
+			 usleep((frames * 1000 * 1000) / output.current_sample_rate);
 			frames = 0;
 		} else {
 			usleep((FRAME_BLOCK * 1000 * 1000) / output.current_sample_rate);

+ 163 - 0
main/output_dac.c.tes

@@ -0,0 +1,163 @@
+#include "squeezelite.h"
+#include "driver/i2s.h"
+
+static log_level loglevel;
+
+static bool running = true;
+
+extern struct outputstate output;
+extern struct buffer *outputbuf;
+extern struct buffer *streambuf;
+
+#define LOCK   mutex_lock(outputbuf->mutex)
+#define UNLOCK mutex_unlock(outputbuf->mutex)
+
+#define FRAME_BLOCK MAX_SILENCE_FRAMES
+
+extern u8_t *silencebuf;
+extern u8_t *buf;
+
+#define I2S_NUM         (0)
+#define WAVE_FREQ_HZ    (100)
+#define PI              (3.14159265)
+#define I2S_BCK_IO      (GPIO_NUM_26)
+#define I2S_WS_IO       (GPIO_NUM_25)
+#define I2S_DO_IO       (GPIO_NUM_22)
+#define I2S_DI_IO       (-1)
+// buffer length is expressed in number of samples
+#define I2S_BUF_LEN		60
+
+static int _write_frames(frames_t out_frames, bool silence, s32_t gainL,
+		s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out,
+		ISAMPLE_T **cross_ptr);
+
+void set_volume(unsigned left, unsigned right) {
+	LOG_DEBUG("setting internal gain left: %u right: %u", left, right);
+	LOCK;
+	output.gainL = left;
+	output.gainR = right;
+	// TODO
+	output.gainL = FIXED_ONE;
+	output.gainR = FIXED_ONE;
+	UNLOCK;
+}
+
+static void *output_thread(void *arg) {
+	bool start = true;
+	bool output_off = (output.state == OUTPUT_OFF);
+	bool probe_device = (arg != NULL);
+	int err;
+
+	while (running) {
+		// todo: implement output off logic?
+		// todo: call i2s_set_clock here if rate is changed
+		LOCK;
+
+		output.device_frames = 0;
+		output.updated = gettime_ms();
+		output.frames_played_dmp = output.frames_played;
+
+		_output_frames(I2S_BUF_LEN*2); // fill at least one DMA buffer with stereo signal
+
+		UNLOCK;
+	}
+
+	return 0;
+}
+
+static pthread_t thread;
+
+void output_init_dac(log_level level, char *device, unsigned output_buf_size,
+		char *params, unsigned rates[], unsigned rate_delay, unsigned idle) {
+	loglevel = level;
+
+	LOG_INFO("init output DAC");
+
+	memset(&output, 0, sizeof(output));
+
+	output.start_frames = 0; //CONFIG_ //FRAME_BLOCK * 2;
+	output.write_cb = &_write_frames;
+	output.rate_delay = rate_delay;
+
+	// ensure output rate is specified to avoid test open
+	if (!rates[0]) {
+		rates[0] = 44100;
+	}
+	device = "DAC";
+	output_init_common(level, device, output_buf_size, rates, idle);
+
+	i2s_config_t i2s_config = {
+			.mode = I2S_MODE_MASTER | I2S_MODE_TX,                    // Only TX
+			.sample_rate = output.current_sample_rate,
+			.bits_per_sample = BYTES_PER_FRAME * 8,
+			.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,           //2-channels
+			.communication_format = I2S_COMM_FORMAT_I2S
+					| I2S_COMM_FORMAT_I2S_MSB,
+			.dma_buf_count = 6, //todo: tune this parameter. Expressed in numbrer of buffers
+			.dma_buf_len = I2S_BUF_LEN, // todo: tune this parameter. Expressed in number of samples. Byte size depends on bit depth
+			.use_apll = false,
+			.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1
+			};
+	i2s_pin_config_t pin_config = { .bck_io_num = I2S_BCK_IO, .ws_io_num =
+			I2S_WS_IO, .data_out_num = I2S_DO_IO, .data_in_num = I2S_DI_IO //Not used
+			};
+	i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
+	i2s_set_pin(I2S_NUM, &pin_config);
+	i2s_set_clk(I2S_NUM, output.current_sample_rate, i2s_config.bits_per_sample, 2);
+
+#if LINUX || OSX || FREEBSD || POSIX
+	pthread_attr_t attr;
+	pthread_attr_init(&attr);
+#ifdef PTHREAD_STACK_MIN
+	pthread_attr_setstacksize(&attr,
+			PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE);
+#endif
+	pthread_create(&thread, &attr, output_thread, NULL);
+	pthread_attr_destroy(&attr);
+#endif
+#if WIN
+	thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&output_thread, NULL, 0, NULL);
+#endif
+
+}
+
+void output_close_dac(void) {
+	LOG_INFO("close output");
+	LOCK;
+	running = false;
+	UNLOCK;
+
+	output_close_common();
+}
+
+static int _write_frames(frames_t out_frames, bool silence, s32_t gainL,
+		s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out,
+		ISAMPLE_T **cross_ptr) {
+
+	u8_t *obuf;
+	size_t i2s_bytes_write = 0;
+
+	if (!silence) {
+
+		if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) {
+			_apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr);
+		}
+
+		obuf = outputbuf->readp;
+
+	} else {
+
+		obuf = silencebuf;
+	}
+
+	//_scale_and_pack_frames(buf + buffill * bytes_per_frame, (s32_t *)(void *)obuf, out_frames, gainL, gainR, output.format);
+
+//	buffill += out_frames;
+	   i2s_write(I2S_NUM, obuf, out_frames  *BYTES_PER_FRAME, &i2s_bytes_write, 100);
+
+
+
+	return (int)i2s_bytes_write * BYTES_PER_FRAME;
+
+}
+

+ 13 - 2
main/squeezelite.h

@@ -81,6 +81,9 @@
 #if defined(DACAUDIO)
 #undef DACAUDIO
 #define DACAUDIO  1
+#elif defined(BTAUDIO)
+#undef BTAUDIO
+#define BTAUDIO 1
 #elif LINUX && !defined(PORTAUDIO)
 #define ALSA      1
 #define PORTAUDIO 0
@@ -738,8 +741,7 @@ void _pa_open(void);
 #endif
 
 // output_dac.c
-// todo: do we need a distinction between DACAUDIO and BTAUDIO?
-#if DACAUDIO || BTAUDIO
+#if DACAUDIO
 void set_volume(unsigned left, unsigned right);
 bool test_open(const char *device, unsigned rates[], bool userdef_rates);
 void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle);
@@ -747,6 +749,15 @@ void output_close_dac(void);
 void hal_bluetooth_init(log_level loglevel);
 #endif
 
+//output_bt.c
+#if  BTAUDIO
+void set_volume(unsigned left, unsigned right);
+bool test_open(const char *device, unsigned rates[], bool userdef_rates);
+void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle);
+void output_close_bt(void);
+void hal_bluetooth_init(log_level loglevel);
+#endif
+
 
 // output_stdout.c
 void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay);