components/driver/i2s.c
/**
 * @brief   Write data to I2S DMA transmit buffer while expanding the number of bits per sample. For example, expanding 16-bit PCM to 32-bit PCM.
 * @note    Many ticks pass without space becoming available in the DMA
 *          transmit buffer, then the function will return (note that if the
 *          data is written to the DMA buffer in pieces, the overall operation
 *          may still take longer than this timeout.) Pass portMAX_DELAY for no
 *          timeout.
 *          Format of the data in source buffer is determined by the I2S configuration (see i2s_config_t).
 *
 * @param       i2s_num             I2S device number
 * @param       src                 Source address to write from
 * @param       size                Size of data in bytes
 * @param       src_bits            Source audio bit
 * @param       aim_bits            Bit wanted, no more than 32, and must be greater than src_bits
 * @param[out]  bytes_written       Number of bytes written, if timeout, the result will be less than the size passed in.
 * @param       ticks_to_wait       TX buffer wait timeout in RTOS ticks. If this
 * @return
 *     - ESP_OK              Success
 *     - ESP_ERR_INVALID_ARG Parameter error
 */
@brief   Write data to I2S DMA transmit buffer while expanding the number of bits per sample. For example, expanding 16-bit PCM to 32-bit PCM.
@note    Many ticks pass without space becoming available in the DMA
transmit buffer, then the function will return (note that if the
data is written to the DMA buffer in pieces, the overall operation
may still take longer than this timeout.) Pass portMAX_DELAY for no
timeout.
Format of the data in source buffer is determined by the I2S configuration .


@brief Write data to I2S DMA transmit buffer while expanding the number of bits per sample . For example expanding 16 - bit PCM to 32 - bit PCM . @note Many ticks pass without space becoming available in the DMA transmit buffer then the function will return ( note that if the data is written to the DMA buffer in pieces the overall operation may still take longer than this timeout . ) Pass portMAX_DELAY for no timeout . Format of the data in source buffer is determined by the I2S configuration .
esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, size_t src_bits, size_t aim_bits, size_t *bytes_written, TickType_t ticks_to_wait)
{
    esp_err_t ret = ESP_OK;
    char *data_ptr;
    int bytes_can_write, tail;
    int src_bytes, aim_bytes, zero_bytes;
    *bytes_written = 0;
    ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error");
    ESP_RETURN_ON_FALSE((size > 0), ESP_ERR_INVALID_ARG, TAG, "size must greater than zero");
    ESP_RETURN_ON_FALSE((aim_bits >= src_bits), ESP_ERR_INVALID_ARG, TAG, "aim_bits mustn't be less than src_bits");
    ESP_RETURN_ON_FALSE((p_i2s[i2s_num]->tx), ESP_ERR_INVALID_ARG, TAG, "TX mode is not enabled");
    if (src_bits < I2S_BITS_PER_SAMPLE_8BIT || aim_bits < I2S_BITS_PER_SAMPLE_8BIT) {
        ESP_LOGE(TAG, "bits mustn't be less than 8, src_bits %d aim_bits %d", src_bits, aim_bits);
        return ESP_ERR_INVALID_ARG;
    }
    if (src_bits > I2S_BITS_PER_SAMPLE_32BIT || aim_bits > I2S_BITS_PER_SAMPLE_32BIT) {
        ESP_LOGE(TAG, "bits mustn't be greater than 32, src_bits %d aim_bits %d", src_bits, aim_bits);
        return ESP_ERR_INVALID_ARG;
    }
    if ((src_bits == I2S_BITS_PER_SAMPLE_16BIT || src_bits == I2S_BITS_PER_SAMPLE_32BIT) && (size % 2 != 0)) {
        ESP_LOGE(TAG, "size must be a even number while src_bits is even, src_bits %d size %d", src_bits, size);
        return ESP_ERR_INVALID_ARG;
    }
    if (src_bits == I2S_BITS_PER_SAMPLE_24BIT && (size % 3 != 0)) {
        ESP_LOGE(TAG, "size must be a multiple of 3 while src_bits is 24, size %d", size);
        return ESP_ERR_INVALID_ARG;
    }
    src_bytes = src_bits / 8;
    aim_bytes = aim_bits / 8;
    zero_bytes = aim_bytes - src_bytes;
    xSemaphoreTake(p_i2s[i2s_num]->tx->mux, (portTickType)portMAX_DELAY);
    size = size * aim_bytes / src_bytes;
    ESP_LOGD(TAG, "aim_bytes %d src_bytes %d size %d", aim_bytes, src_bytes, size);
    while (size > 0) {
        if (p_i2s[i2s_num]->tx->rw_pos == p_i2s[i2s_num]->tx->buf_size || p_i2s[i2s_num]->tx->curr_ptr == NULL) {
            if (xQueueReceive(p_i2s[i2s_num]->tx->queue, &p_i2s[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) {
                ret = ESP_ERR_TIMEOUT;
                break;
            }
            p_i2s[i2s_num]->tx->rw_pos = 0;
        }
        data_ptr = (char *)p_i2s[i2s_num]->tx->curr_ptr;
        data_ptr += p_i2s[i2s_num]->tx->rw_pos;
        bytes_can_write = p_i2s[i2s_num]->tx->buf_size - p_i2s[i2s_num]->tx->rw_pos;
        if (bytes_can_write > (int)size) {
            bytes_can_write = size;
        }
        tail = bytes_can_write % aim_bytes;
        bytes_can_write = bytes_can_write - tail;
        memset(data_ptr, 0, bytes_can_write);
        for (int j = 0; j < bytes_can_write; j += (aim_bytes - zero_bytes)) {
            j += zero_bytes;
            memcpy(&data_ptr[j], (const char *)(src + *bytes_written), aim_bytes - zero_bytes);
            (*bytes_written) += (aim_bytes - zero_bytes);
        }
        size -= bytes_can_write;
        p_i2s[i2s_num]->tx->rw_pos += bytes_can_write;
    }
    xSemaphoreGive(p_i2s[i2s_num]->tx->mux);
    return ret;
}
esp_err_t i2s_write_expand ( i2s_port_t i2s_num , const void * src , size_t size , size_t src_bits , size_t aim_bits , size_t * bytes_written , TickType_t ticks_to_wait ) { esp_err_t ret = ESP_OK ; char * data_ptr ; int bytes_can_write , tail ; int src_bytes , aim_bytes , zero_bytes ; * bytes_written = 0 ; ESP_RETURN_ON_FALSE ( ( i2s_num < I2S_NUM_MAX ) , ESP_ERR_INVALID_ARG , TAG , " " ) ; ESP_RETURN_ON_FALSE ( ( size > 0 ) , ESP_ERR_INVALID_ARG , TAG , " " ) ; ESP_RETURN_ON_FALSE ( ( aim_bits >= src_bits ) , ESP_ERR_INVALID_ARG , TAG , " " ) ; ESP_RETURN_ON_FALSE ( ( p_i2s [ i2s_num ] -> tx ) , ESP_ERR_INVALID_ARG , TAG , " " ) ; if ( src_bits < I2S_BITS_PER_SAMPLE_8BIT || aim_bits < I2S_BITS_PER_SAMPLE_8BIT ) { ESP_LOGE ( TAG , " " , src_bits , aim_bits ) ; return ESP_ERR_INVALID_ARG ; } if ( src_bits > I2S_BITS_PER_SAMPLE_32BIT || aim_bits > I2S_BITS_PER_SAMPLE_32BIT ) { ESP_LOGE ( TAG , " " , src_bits , aim_bits ) ; return ESP_ERR_INVALID_ARG ; } if ( ( src_bits == I2S_BITS_PER_SAMPLE_16BIT || src_bits == I2S_BITS_PER_SAMPLE_32BIT ) && ( size % 2 != 0 ) ) { ESP_LOGE ( TAG , " " , src_bits , size ) ; return ESP_ERR_INVALID_ARG ; } if ( src_bits == I2S_BITS_PER_SAMPLE_24BIT && ( size % 3 != 0 ) ) { ESP_LOGE ( TAG , " " , size ) ; return ESP_ERR_INVALID_ARG ; } src_bytes = src_bits / 8 ; aim_bytes = aim_bits / 8 ; zero_bytes = aim_bytes - src_bytes ; xSemaphoreTake ( p_i2s [ i2s_num ] -> tx -> mux , ( portTickType ) portMAX_DELAY ) ; size = size * aim_bytes / src_bytes ; ESP_LOGD ( TAG , " " , aim_bytes , src_bytes , size ) ; while ( size > 0 ) { if ( p_i2s [ i2s_num ] -> tx -> rw_pos == p_i2s [ i2s_num ] -> tx -> buf_size || p_i2s [ i2s_num ] -> tx -> curr_ptr == NULL ) { if ( xQueueReceive ( p_i2s [ i2s_num ] -> tx -> queue , & p_i2s [ i2s_num ] -> tx -> curr_ptr , ticks_to_wait ) == pdFALSE ) { ret = ESP_ERR_TIMEOUT ; break ; } p_i2s [ i2s_num ] -> tx -> rw_pos = 0 ; } data_ptr = ( char * ) p_i2s [ i2s_num ] -> tx -> curr_ptr ; data_ptr += p_i2s [ i2s_num ] -> tx -> rw_pos ; bytes_can_write = p_i2s [ i2s_num ] -> tx -> buf_size - p_i2s [ i2s_num ] -> tx -> rw_pos ; if ( bytes_can_write > ( int ) size ) { bytes_can_write = size ; } tail = bytes_can_write % aim_bytes ; bytes_can_write = bytes_can_write - tail ; memset ( data_ptr , 0 , bytes_can_write ) ; for ( int j = 0 ; j < bytes_can_write ; j += ( aim_bytes - zero_bytes ) ) { j += zero_bytes ; memcpy ( & data_ptr [ j ] , ( const char * ) ( src + * bytes_written ) , aim_bytes - zero_bytes ) ; ( * bytes_written ) += ( aim_bytes - zero_bytes ) ; } size -= bytes_can_write ; p_i2s [ i2s_num ] -> tx -> rw_pos += bytes_can_write ; } xSemaphoreGive ( p_i2s [ i2s_num ] -> tx -> mux ) ; return ret ; }
@brief   Write data to I2S DMA transmit buffer while expanding the number of bits per sample.
@brief Write data to I2S DMA transmit buffer while expanding the number of bits per sample .