Problems configuring I2S to receive from mono source.

Hi all. 

I've attached a single channel microphone  onto my nRF9151 and I'd like to capture blocks of audio for processing. I've configured the device tree like this:

i2s: &i2s0 {
  compatible = "nordic,nrf-i2s";
  pinctrl-0 = <&i2s0_default>;
  pinctrl-names = "default"; // , "sleep";
  clock-source = "PCLK32M_HFXO";
  status = "okay";
};

&pinctrl {
  i2s0_default: i2s0_default {
  group1 {
    psels = <NRF_PSEL(I2S_SCK_M, 0, 25)>,
                <NRF_PSEL(I2S_LRCK_M, 0, 23)>,
                <NRF_PSEL(I2S_SDIN, 0, 24)>;
    };
  };
}

Apologies for formatting - it's corrupting the symbols somehow. Then in a simple main.c, I put this:

	#define I2S_DEV 	DT_NODELABEL(i2s)
	static const struct device *i2s = DEVICE_DT_GET(I2S_DEV);
    
    ...

        #define SAMPLE_FREQUENCY    44100
		#define SAMPLE_BIT_WIDTH    24
		#define BYTES_PER_SAMPLE    sizeof(int32_t)
		#define NUMBER_OF_CHANNELS  2
		#define SAMPLES_PER_BLOCK   ((SAMPLE_FREQUENCY / 1000) * NUMBER_OF_CHANNELS)
		#define INITIAL_BLOCKS      2
		#define TIMEOUT             1000

		#define BLOCK_SIZE  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
		#define BLOCK_COUNT (INITIAL_BLOCKS + 4)
		K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4);


		if (!device_is_ready(i2s)) {
			printk("I2S device %s is not ready!\n\r", i2s->name);
			// return -1;
		}


        // Configure I2S
		struct i2s_config config = {
			.word_size = SAMPLE_BIT_WIDTH,											// 24 bit as ICS43434 Datasheet.
			.channels = NUMBER_OF_CHANNELS,											// 1 ch - single ICS43434 (left first! WS = 0).
			.format = I2S_FMT_DATA_FORMAT_I2S, 						                // Data on Left Ch - While WS is LOW
			.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,			// nRF drives bit clk (SCK) & frame clk (WS).
			.frame_clk_freq = SAMPLE_FREQUENCY,										// 44kHz as usual.
			.mem_slab = &mem_slab,													// 
			.block_size = BLOCK_SIZE,												// 
			.timeout = TIMEOUT														// 
		};

		ret = i2s_configure(i2s, I2S_DIR_RX, &config);
		if (ret < 0) {
			printk("Failed to configure I2S RX stream: %d\n", ret);
			return false;
		}
		

		// Trigger ONCE - outside the loop
		ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_START);
		if (ret < 0) {
			printk("Failed to trigger START: %d\n", ret);
			return false;
		}

		// Read... 
		while(1) {
			void *mem_block;
			uint32_t block_size;

			// read
			ret = i2s_read(i2s, &mem_block, &block_size);
			if (ret < 0) {
				printk("Failed to read data: %d\n", ret);
				break;
			}

			// Debug: Print block info and sample data
			int32_t *samples = (int32_t *)mem_block;
			char buf[100];
			sprintf(buf, "BUF=%d %d %d %d %d %d %d %d\n", 
				samples[0], samples[1], samples[2], samples[3],
				samples[4], samples[5], samples[6], samples[7]);
			printk(buf);
		}

On the oscilloscope, everything looks great. WS, SCLK & SD are perfect. If I make a bit of noise, I can see the 24-bit data flipping higher bits etc. However, I can't get anything in the code. All zeros!? Any idea what's going on here? I'm sure it's something very basic I'm missing. 
NB - I set it up originally as num channels = 1 - same result. And I opted for 4 bytes per sample to align for simpler memory manipulation later. 
Parents
  • Did you bother to check the program log output? Your code is not processing the buffers correctly - the required k_mem_slab_free() call is missing completely. The driver stops recording data once it runs out of buffer slabs.

    I strongly recommend looking at existing I²S sample code provided in the NCS SDK.

  • Hi there. Thanks for your suggestions.

    I think I'm close to figuring it out. Sorry, wasn't the mem free - I had just temporarily moved that. Seems the first couple of blocks of input data are always zero for some reason. Which may explain this bizarre way of defining the block count (from samples which I am using btw) :

    #define BLOCK_COUNT (INITIAL_BLOCKS + CONFIG_EXTRA_BLOCKS)
    I have to infer that Config_extra_blocks must be dropped before we get to the real data. 
    Having factored that in and done a bit more work:
    #define SAMPLE_FREQUENCY    44100
    		#define SAMPLE_BIT_WIDTH    24
    		#define BYTES_PER_SAMPLE    sizeof(int32_t)
    		#define NUMBER_OF_CHANNELS  1
    		/* Such block length provides an echo with the delay of 10 ms. */
    		#define SAMPLES_PER_BLOCK   ((SAMPLE_FREQUENCY / 100) * NUMBER_OF_CHANNELS)
    		#define TIMEOUT             1000
    		#define BLOCK_SIZE  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
    		#define BLOCKS_TO_DROP 2 // First ~2 blocks are always full of bollocks
    		#define BLOCK_COUNT (6 + BLOCKS_TO_DROP) 
    
    		K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4);
    
    		if (!device_is_ready(i2s)) {
    			printk("I2S device %s is not ready!\n\r", i2s->name);
    			return -1;
    		}
    
    		struct i2s_config config = {
    			.word_size = SAMPLE_BIT_WIDTH,											// 24 bit as ICS43434 Datasheet.
    			.channels = NUMBER_OF_CHANNELS,											// 1 ch - single ICS43434 (left first! WS = 0).
    			.format = I2S_FMT_DATA_FORMAT_I2S, 						                // Data on Left Ch - While WS is LOW
    			.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,			// nRF drives bit clk (SCK) & frame clk (WS).
    			.frame_clk_freq = SAMPLE_FREQUENCY,										// 44kHz as usual.
    			.mem_slab = &mem_slab,													// 
    			.block_size = BLOCK_SIZE,												// 
    			.timeout = TIMEOUT														// 
    		};
    
    		// Configure I2S device
    		ret = i2s_configure(i2s, I2S_DIR_RX, &config);
    
    
    		// Start I2S
    		ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_START);
    		k_msleep(1000);  // Let DMA fill initial buffers
    
    
    		// Read continuously - blocks fill as you read them
    		for (int loop = 0; loop < 6; loop++) {
    			volatile  void *mem_block;
    			volatile  uint32_t block_size;
    
    			ret = i2s_read(i2s, &mem_block, &block_size);
    			if (ret < 0) {
    				printk("I2S read failed: %d\n", ret);
    				break;
    			}
    
    			if (loop < 2) {
    				// Discard first 2 garbage blocks
    				k_mem_slab_free(&mem_slab, &mem_block);
    				continue;
    			}
    			
    			// Process valid blocks 3-6
    			// ...the good stuff
    			k_mem_slab_free(&mem_slab, &mem_block);
    		}
    
    		ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_STOP);
    I can now see plausible looking data in the debug watch tab. But the i2s_read is now always failing after 4 successful reads. Even if I increase the block count... One potentially relevant observation is that I can see on the scope that the data only flows for a little while after the trigger start. I assume that all 8 blocks are filled during this time?! Or is there some weird ping pong 2 by 2 block magic going on in that no one thought to document?
  • Look at k_mem_slab_free docs, I am 90% sure these calls are not coded correctly. It expects "pointer to mem block" and not "pointer to pointer to mem block" aka void* vs. void** in C.

    As I said previously, you absolutely need to look at existing sample code here.

  • You are right about the mem free definition. Thank you for spotting that error. 

    I can assure you I am following the samples - since there is little else to go on. But I'm afraid finding and using an example from within the entire Zephyr library is a bit like hunting for a chicken drumstick in the local dump. There are a few hidden in there but not all are safe to eat. 

    There are a number of things going on that I do not understand. Looking at my updated code:

    -

    #define SAMPLE_FREQUENCY    44100
    		#define SAMPLE_BIT_WIDTH    24 // XXXX 16 for test.
    		#define BYTES_PER_SAMPLE    sizeof(int32_t) // XXXX uint32_t for test.
    		#define NUMBER_OF_CHANNELS  1
    		/* Such block length provides an echo with the delay of 10 ms. */
    		#define SAMPLES_PER_BLOCK   ((SAMPLE_FREQUENCY / 100) * NUMBER_OF_CHANNELS)
    		#define TIMEOUT             1000
    		#define BLOCK_SIZE  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
    		#define BLOCKS_INTERNAL_USE 2 // First ~2 blocks are always full of bollocks
    		#define BLOCK_COUNT (8 + BLOCKS_INTERNAL_USE)
    
    		// Define & init memory slab & its buffer - with 
    		K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4);
    
    		if (!device_is_ready(i2s)) {
    			printk("I2S device %s is not ready!\n\r", i2s->name);
    			return -1;
    		}
    
    		struct i2s_config config = {
    			.word_size = SAMPLE_BIT_WIDTH,											// 24 bit as ICS43434 Datasheet.
    			.channels = NUMBER_OF_CHANNELS,											// 1 ch - single ICS43434 (left first! WS = 0).
    			.format = I2S_FMT_DATA_FORMAT_I2S, 						                // Data on Left Ch - While WS is LOW
    			.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,			// nRF drives bit clk (SCK) & frame clk (WS).
    			.frame_clk_freq = SAMPLE_FREQUENCY,										// 44kHz as usual.
    			.mem_slab = &mem_slab,													// 
    			.block_size = BLOCK_SIZE,												// 
    			.timeout = TIMEOUT														// 
    		};
    
    		// Configure I2S device
    		ret = i2s_configure(i2s, I2S_DIR_RX, &config);
    		if (ret < 0) {
    			printk("Failed to configure I2S RX stream: %d\n", ret);
    			return -1;
    		}
    
    
    		// Add these as global variables for debugger visibility
    		volatile uint32_t debug_total_blocks = BLOCK_COUNT;
    		volatile uint32_t debug_used_blocks = 0;
    		volatile uint32_t debug_free_blocks = 0;
    		volatile void* debug_last_block_ptr = NULL;
    		volatile size_t debug_last_block_size = 0;
    		volatile int debug_loop_count = 0;
    
    
    		for (int t = 0; t < 2; t++) {
    			k_msleep(1000);  // Let DMA fill initial buffers
    			
    			// Start I2S
    			ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_START);
    			k_msleep(1000);  // Let DMA fill initial buffers
    
    
    			// Read continuously - blocks fill as you read them
    			for (int loop = 0; loop < 6; loop++) {
    				void *mem_block;
    				size_t block_size;
    
    				debug_loop_count = loop;
    				debug_used_blocks = mem_slab.info.num_used;
    				debug_free_blocks = mem_slab.info.num_blocks - mem_slab.info.num_used;
    
    				ret = i2s_read(i2s, &mem_block, &block_size);
    				if (ret < 0) {
    					printk("I2S read failed: %d\n", ret);
    					break;
    				}
    
    			
    				
    				// ret = i2s_read(i2s, &mem_block, &block_size);
    				debug_last_block_ptr = mem_block;
    				debug_last_block_size = block_size;
    
    				if (loop < 2) {
    					// Discard first 2 garbage blocks
    					k_mem_slab_free(&mem_slab, mem_block);
    					continue;
    				}
    				
    				// Process valid blocks 3-6
    				// ... your processing code
    				k_mem_slab_free(&mem_slab, mem_block);
    			}
    
    			ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_STOP);
    			
    			// CRITICAL: Start doesnt' do anything without this???
    			if (t < 1) {  // Don't prepare after last iteration
    				ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_PREPARE);
    			}
    			
    			k_msleep(1000);  // Let DMA fill initial buffers
    		}

    -

    1. Increasing block count has no effect on the apparent number of blocks collected. The maximum appears to be 4. On a scope capture, the I2S bus chatter looks like this. Crappy picture, but note 5ms per time division = about 49 ms of clock activity, and 33 ms of actual data. I'm expected 10ms per block. If I run through my code (with Block count set to 8), it tells me that 4 blocks were filled and 4 left empty. After dumping 2, I'm left with 2. I can process and free those. Why does this happen? Is there a Nordic hardware DMA limitation that is not known to Zephyr? I'm sure it's completely expected, but can anyone explain this behaviour? And how would it change with changes to the sampling duration and count? I.e. is the amount of data to dump a fixed duration, or function of block count? Is it documented anywhere? I could continue to rebuild an infinite number of configurations and check each one on the scope, but I'd rather punch myself in the face.

    If I had to guess, I'd say it spins up the clock for 1 block duration to wake the I2S device - then it captures 2 blocks (with the understanding that they will probably contain garbage), then starts actual data collection (total of 5 blocks for 2 blocks of useful data). Which would be fine if a) it was documented and b) it allowed you to capture more than 2 actual blocks of data to compensate for all of the incumbent hassle. 

    edit: tested with 20ms block. total clock activity ~ 98 ms. data present for 83 ms. Therefore - above assumptions seem valid.

    edit: I suppose having increased the block size to 20, I could now avail myself of all 4 captured blocks the init time having been empirically determined to be about 17ms in this case... Would probably be better to add an interrupt into the driver that triggers after a configurable delay from when data starts coming in... 

    -

    -

    2. After processing and freeing the 4 blocks, I execute a trigger STOP command. Although the exact usage of this is not clear - I don't think it's actually doing anything in this case. I did find that the subsequent start would not work effect unless I added a PREPARE cmd after each loop - presumably recovering from an error condition (??? clue ???). With that, it will go round the loop again, collecting and processing a further 4 (2 useful) blocks... While I'm delighted that it's collecting and allowing me to do something with the blocks, having to reverse engineer the whole thing like this is making me miserable and wasting a lot of time. 

Reply
  • You are right about the mem free definition. Thank you for spotting that error. 

    I can assure you I am following the samples - since there is little else to go on. But I'm afraid finding and using an example from within the entire Zephyr library is a bit like hunting for a chicken drumstick in the local dump. There are a few hidden in there but not all are safe to eat. 

    There are a number of things going on that I do not understand. Looking at my updated code:

    -

    #define SAMPLE_FREQUENCY    44100
    		#define SAMPLE_BIT_WIDTH    24 // XXXX 16 for test.
    		#define BYTES_PER_SAMPLE    sizeof(int32_t) // XXXX uint32_t for test.
    		#define NUMBER_OF_CHANNELS  1
    		/* Such block length provides an echo with the delay of 10 ms. */
    		#define SAMPLES_PER_BLOCK   ((SAMPLE_FREQUENCY / 100) * NUMBER_OF_CHANNELS)
    		#define TIMEOUT             1000
    		#define BLOCK_SIZE  (BYTES_PER_SAMPLE * SAMPLES_PER_BLOCK)
    		#define BLOCKS_INTERNAL_USE 2 // First ~2 blocks are always full of bollocks
    		#define BLOCK_COUNT (8 + BLOCKS_INTERNAL_USE)
    
    		// Define & init memory slab & its buffer - with 
    		K_MEM_SLAB_DEFINE_STATIC(mem_slab, BLOCK_SIZE, BLOCK_COUNT, 4);
    
    		if (!device_is_ready(i2s)) {
    			printk("I2S device %s is not ready!\n\r", i2s->name);
    			return -1;
    		}
    
    		struct i2s_config config = {
    			.word_size = SAMPLE_BIT_WIDTH,											// 24 bit as ICS43434 Datasheet.
    			.channels = NUMBER_OF_CHANNELS,											// 1 ch - single ICS43434 (left first! WS = 0).
    			.format = I2S_FMT_DATA_FORMAT_I2S, 						                // Data on Left Ch - While WS is LOW
    			.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER,			// nRF drives bit clk (SCK) & frame clk (WS).
    			.frame_clk_freq = SAMPLE_FREQUENCY,										// 44kHz as usual.
    			.mem_slab = &mem_slab,													// 
    			.block_size = BLOCK_SIZE,												// 
    			.timeout = TIMEOUT														// 
    		};
    
    		// Configure I2S device
    		ret = i2s_configure(i2s, I2S_DIR_RX, &config);
    		if (ret < 0) {
    			printk("Failed to configure I2S RX stream: %d\n", ret);
    			return -1;
    		}
    
    
    		// Add these as global variables for debugger visibility
    		volatile uint32_t debug_total_blocks = BLOCK_COUNT;
    		volatile uint32_t debug_used_blocks = 0;
    		volatile uint32_t debug_free_blocks = 0;
    		volatile void* debug_last_block_ptr = NULL;
    		volatile size_t debug_last_block_size = 0;
    		volatile int debug_loop_count = 0;
    
    
    		for (int t = 0; t < 2; t++) {
    			k_msleep(1000);  // Let DMA fill initial buffers
    			
    			// Start I2S
    			ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_START);
    			k_msleep(1000);  // Let DMA fill initial buffers
    
    
    			// Read continuously - blocks fill as you read them
    			for (int loop = 0; loop < 6; loop++) {
    				void *mem_block;
    				size_t block_size;
    
    				debug_loop_count = loop;
    				debug_used_blocks = mem_slab.info.num_used;
    				debug_free_blocks = mem_slab.info.num_blocks - mem_slab.info.num_used;
    
    				ret = i2s_read(i2s, &mem_block, &block_size);
    				if (ret < 0) {
    					printk("I2S read failed: %d\n", ret);
    					break;
    				}
    
    			
    				
    				// ret = i2s_read(i2s, &mem_block, &block_size);
    				debug_last_block_ptr = mem_block;
    				debug_last_block_size = block_size;
    
    				if (loop < 2) {
    					// Discard first 2 garbage blocks
    					k_mem_slab_free(&mem_slab, mem_block);
    					continue;
    				}
    				
    				// Process valid blocks 3-6
    				// ... your processing code
    				k_mem_slab_free(&mem_slab, mem_block);
    			}
    
    			ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_STOP);
    			
    			// CRITICAL: Start doesnt' do anything without this???
    			if (t < 1) {  // Don't prepare after last iteration
    				ret = i2s_trigger(i2s, I2S_DIR_RX, I2S_TRIGGER_PREPARE);
    			}
    			
    			k_msleep(1000);  // Let DMA fill initial buffers
    		}

    -

    1. Increasing block count has no effect on the apparent number of blocks collected. The maximum appears to be 4. On a scope capture, the I2S bus chatter looks like this. Crappy picture, but note 5ms per time division = about 49 ms of clock activity, and 33 ms of actual data. I'm expected 10ms per block. If I run through my code (with Block count set to 8), it tells me that 4 blocks were filled and 4 left empty. After dumping 2, I'm left with 2. I can process and free those. Why does this happen? Is there a Nordic hardware DMA limitation that is not known to Zephyr? I'm sure it's completely expected, but can anyone explain this behaviour? And how would it change with changes to the sampling duration and count? I.e. is the amount of data to dump a fixed duration, or function of block count? Is it documented anywhere? I could continue to rebuild an infinite number of configurations and check each one on the scope, but I'd rather punch myself in the face.

    If I had to guess, I'd say it spins up the clock for 1 block duration to wake the I2S device - then it captures 2 blocks (with the understanding that they will probably contain garbage), then starts actual data collection (total of 5 blocks for 2 blocks of useful data). Which would be fine if a) it was documented and b) it allowed you to capture more than 2 actual blocks of data to compensate for all of the incumbent hassle. 

    edit: tested with 20ms block. total clock activity ~ 98 ms. data present for 83 ms. Therefore - above assumptions seem valid.

    edit: I suppose having increased the block size to 20, I could now avail myself of all 4 captured blocks the init time having been empirically determined to be about 17ms in this case... Would probably be better to add an interrupt into the driver that triggers after a configurable delay from when data starts coming in... 

    -

    -

    2. After processing and freeing the 4 blocks, I execute a trigger STOP command. Although the exact usage of this is not clear - I don't think it's actually doing anything in this case. I did find that the subsequent start would not work effect unless I added a PREPARE cmd after each loop - presumably recovering from an error condition (??? clue ???). With that, it will go round the loop again, collecting and processing a further 4 (2 useful) blocks... While I'm delighted that it's collecting and allowing me to do something with the blocks, having to reverse engineer the whole thing like this is making me miserable and wasting a lot of time. 

Children
  • The k_msleep(1000) after the start trigger call is about 2 orders of magnitude too long - the driver runs out of buffers within that time already. Your total buffer space is only roughly 100ms long!

    Try removing this function call entirely, i2s_read() waits for a filled buffer IIRC.

    One second of high quality audio is a lot of data for NRF MCUs, you don't typically want that much buffer here unless its absolutely required for your application.

  • Yeah, the delay is huge - remember this is jut a sand pit to feel out how the peripheral behaves, before writing proper code for the application. But all the more reason why it's odd that the I2S only runs for 4 blocks. Even if I set block_count to 10, the debug variables tell me that only 4 are in use (6 are free) after the START trigger. Then as I read, it decreases one at a time (as expected). 

    Then it requires a PREPARE trigger for a further START to have any effect. Which suggests that an exception has been handled somewhere resulting in a state change from running -> error. 

    Update: I've gone rooting around inside i2snrfx.c just out of curiosity, and noticed this config `CONFIG_I2S_NRFX_RX_BLOCK_COUNT ` being used. Seems important?! So, I added an assignment to it in my main.c and ctrl clicked to see where it went:

    #define CONFIG_I2C_NRFX 1
    #define CONFIG_I2C_NRFX_TWIM 1
    #define CONFIG_I2C_NRFX_TRANSFER_TIMEOUT 500
    #define CONFIG_I2C_INIT_PRIORITY 50
    #define CONFIG_I2S 1
    #define CONFIG_I2S_INIT_PRIORITY 50
    #define CONFIG_I2S_NRFX 1
    #define CONFIG_I2S_NRFX_RX_BLOCK_COUNT 4
    #define CONFIG_I2S_NRFX_TX_BLOCK_COUNT 4
    The above stuff is inside my build folder!? In autoconf.h. It seems a bit rum that this value of 4 is in there. In my prj.conf I only set `CONFIG_I2S=y`: none of that other stuff... It must get pulled in through a Kconfig that I wasn't aware of being applied.
     
    Update:
    This stuff comes from kconfig.nrfx inside of zephyr/drivers/i2s. This confuses me.
    a) I thought I wasn't using the nrf i2s code - but rather the OS agnostic Zephyr library.
    b) The smoking gun is that this hardware config limitation is preventing the Zephyr I2S code from working - but it doesn't set off any alarm bells during build. 
    The fix is something like:
    #define BLOCK_COUNT  MIN(CONFIG_I2S_NRFX_RX_BLOCK_COUNT, 8)
    and maybe add something like this:

    _Static_assert(CONFIG_I2S_NRFX_RX_BLOCK_COUNT >= 4, "Need at least 4 I2S RX blocks");
  • Unless your code is too slow you should never need more than 4 blocks in those config variables.

    These determine the message queue length from the I²S interrupt handler to the application. Pending buffers are removed from this queue in the i2s_read call already. Thus I don't see how a properly designed application would ever have more than 2 of them pending in the queue - you want your audio code to run as fast and as high priority as possible.

    And yes, you want to stop and properly restart audio streaming when the hardware runs out of buffer space. 

    In case your app needs to support unusually high latency you can just add I2S_NRFX_RX_BLOCK_COUNT and I2S_NRFX_TX_BLOCK_COUNT to your prj.conf file. 

    But I would rather think very hard what your basic audio buffer size should be.You can also just simply define big audio buffers that are a second long - should just fit inside the MCU RAM IMHO.

Related