From 4ea9b02b8658eac043807d279f7d56f34993eaea Mon Sep 17 00:00:00 2001 From: hogthrob Date: Tue, 4 Jan 2022 11:41:35 +0100 Subject: [PATCH] Added .clang-format common code formatting style proposal Using this format to format all local source would make merging changes a bit easier and also the code reading should be easier as there would be only one consistent style. --- PlatformIO/.clang-format | 10 + PlatformIO/src/devices/AudioKit.hpp | 672 +++++++++++------------ PlatformIO/src/devices/ES8388Control.cpp | 298 +++++----- PlatformIO/src/devices/ES8388Control.h | 24 +- 4 files changed, 488 insertions(+), 516 deletions(-) create mode 100644 PlatformIO/.clang-format diff --git a/PlatformIO/.clang-format b/PlatformIO/.clang-format new file mode 100644 index 0000000..54f52ca --- /dev/null +++ b/PlatformIO/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: Microsoft +UseTab: Never +IndentWidth: 2 + +# looks nice, but may cause significant changes when an additional statement is addded +## AlignConsecutiveAssignments: AcrossEmptyLinesAndComments + +BreakBeforeBraces: Mozilla + +SortIncludes: Never \ No newline at end of file diff --git a/PlatformIO/src/devices/AudioKit.hpp b/PlatformIO/src/devices/AudioKit.hpp index c570974..35974db 100644 --- a/PlatformIO/src/devices/AudioKit.hpp +++ b/PlatformIO/src/devices/AudioKit.hpp @@ -12,73 +12,71 @@ // with different pinouts for I2S and I2C and different audio controllers // see https://github.com/Ai-Thinker-Open/ESP32-A1S-AudioKit/issues/26 // -// Link to the newer version with ES8388 which brings back usable keys 4 to 6: +// Link to the newer version with ES8388 which brings back usable keys 4 to 6: // https://github.com/Ai-Thinker-Open/ESP32-A1S-AudioKit/files/7387472/esp32-a1s_v2.3-20210508.1.pdf - enum A1SVariant { - ES8388_V1 = 0, - ES8388_V2, - AC101_V1, - UNIDENTIFIED + ES8388_V1 = 0, + ES8388_V2, + AC101_V1, + UNIDENTIFIED }; struct i2c_pin_config_t { - int sda; - int scl; + int sda; + int scl; }; struct A1S_Pinouts { - const char* label; - A1SVariant variant; - i2s_pin_config_t i2s; - i2c_pin_config_t i2c; + const char *label; + A1SVariant variant; + i2s_pin_config_t i2s; + i2c_pin_config_t i2c; }; const A1S_Pinouts a1s_pinouts[] = { // ESP32-A1S initial pinout ES8388 - { - .label = "ES8388 Pinout Variant 1", - .variant = A1SVariant::ES8388_V1, - .i2s = - { - .bck_io_num = 5, - .ws_io_num = 25, - .data_out_num = 26, - .data_in_num = 35, - }, - .i2c = { .sda = 18, .scl = 23 } - }, + {.label = "ES8388 Pinout Variant 1", + .variant = A1SVariant::ES8388_V1, + .i2s = + { + .bck_io_num = 5, + .ws_io_num = 25, + .data_out_num = 26, + .data_in_num = 35, + }, + .i2c = + { + .sda = 18, + .scl = 23, + }}, // ESP32-A1S second pinout ES8388 - { - .label = "ES8388 Pinout Variant 2", - .variant = A1SVariant::ES8388_V2, - .i2s = { - .bck_io_num = 27, - .ws_io_num = 25, - .data_out_num = 26, - .data_in_num = 35, - }, - .i2c = { .sda = 33, .scl = 32 } - }, + {.label = "ES8388 Pinout Variant 2", + .variant = A1SVariant::ES8388_V2, + .i2s = + { + .bck_io_num = 27, + .ws_io_num = 25, + .data_out_num = 26, + .data_in_num = 35, + }, + .i2c = {.sda = 33, .scl = 32}}, // ESP32-A1S AC101 variant - { - .label = "AC101", - .variant = A1SVariant::AC101_V1, - .i2s = { - .bck_io_num = 27, - .ws_io_num = 26, - .data_out_num = 25, - .data_in_num = 35, - }, - .i2c = { .sda = 33, .scl = 32 } - }, + {.label = "AC101", + .variant = A1SVariant::AC101_V1, + .i2s = + { + .bck_io_num = 27, + .ws_io_num = 26, + .data_out_num = 25, + .data_in_num = 35, + }, + .i2c = {.sda = 33, .scl = 32}}, }; - // amplifier enable pin #define GPIO_PA_EN GPIO_NUM_21 #define GPIO_SEL_PA_EN GPIO_SEL_21 @@ -107,394 +105,362 @@ const A1S_Pinouts a1s_pinouts[] = { #define SPEAKER_I2S_NUMBER I2S_NUM_0 - class AudioKit : public Device { public: - AudioKit(); - void init(); - void updateColors(StateColors colors); + AudioKit(); + void init(); + void updateColors(StateColors colors); - void setReadMode(); - void setWriteMode(int sampleRate, int bitDepth, int numChannels); + void setReadMode(); + void setWriteMode(int sampleRate, int bitDepth, int numChannels); - void writeAudio(uint8_t *data, size_t size, size_t *bytes_written); - bool readAudio(uint8_t *data, size_t size); + void writeAudio(uint8_t *data, size_t size, size_t *bytes_written); + bool readAudio(uint8_t *data, size_t size); - void muteOutput(bool mute); + void muteOutput(bool mute); - // ESP-Audio-Kit has speaker and headphone as outputs - void ampOutput(int output); - void setVolume(uint16_t volume); + // ESP-Audio-Kit has speaker and headphone as outputs + void ampOutput(int output); + void setVolume(uint16_t volume); - bool isHotwordDetected(); + bool isHotwordDetected(); - int numAmpOutConfigurations() { return 3; - }; - void updateBrightness(int brightness); + int numAmpOutConfigurations() + { + return 3; + }; + void updateBrightness(int brightness); - const i2s_pin_config_t& getPinConfig() { return a1s_pinouts[variant].i2s; } -private: - void InitI2SSpeakerOrMic(int mode); - AC101 ac; - ES8388Control es8388; + const i2s_pin_config_t &getPinConfig() + { + return a1s_pinouts[variant].i2s; + } +private: + void InitI2SSpeakerOrMic(int mode); + AC101 ac; + ES8388Control es8388; - uint8_t out_vol; - AmpOut out_amp = AMP_OUT_SPEAKERS; + uint8_t out_vol; + AmpOut out_amp = AMP_OUT_SPEAKERS; - A1SVariant variant = UNIDENTIFIED; - bool is_es = false; - bool is_mono_stream_stereo_out = false; - uint16_t key_listen; + A1SVariant variant = UNIDENTIFIED; + bool is_es = false; + bool is_mono_stream_stereo_out = false; + uint16_t key_listen; - IndicatorLight *indicator_light = new IndicatorLight(LED_STREAM, true); + IndicatorLight *indicator_light = new IndicatorLight(LED_STREAM, true); }; AudioKit::AudioKit(){}; void AudioKit::init() { - Serial.print("Identifying Codec... "); - - for (auto pinout: a1s_pinouts) - { - Serial.printf("Checking for %s...\r\n", pinout.label); - if (pinout.variant != AC101_V1) - { - - if ((is_es = es8388.begin(pinout.i2c.sda, pinout.i2c.scl)) == true) - { - variant = pinout.variant; - } - } else { - if (ac.begin(pinout.i2c.sda, pinout.i2c.scl)) - { - variant = pinout.variant; - ac.SetMode(AC101::MODE_ADC_DAC); - } - } - // we are done here now - if (variant != UNIDENTIFIED) { break; } + Serial.print("Identifying Codec... "); + + for (auto pinout : a1s_pinouts) { + Serial.printf("Checking for %s...\r\n", pinout.label); + if (pinout.variant != AC101_V1) { + + if ((is_es = es8388.begin(pinout.i2c.sda, pinout.i2c.scl)) == true) { + variant = pinout.variant; + } + } else { + if (ac.begin(pinout.i2c.sda, pinout.i2c.scl)) { + variant = pinout.variant; + ac.SetMode(AC101::MODE_ADC_DAC); + } } - if (variant!= UNIDENTIFIED) - { - Serial.printf("Found audio controller %s at SDA: %d / SCL: %d\n", a1s_pinouts[variant].label, a1s_pinouts[variant].i2c.sda, a1s_pinouts[variant].i2c.scl); - } else - { - Serial.println("No supported controller found, stopping operation!"); - while (true); // TODO: Better handling for no controller found + // we are done here now + if (variant != UNIDENTIFIED) { + break; } - - key_listen = is_es? ES_KEY_LISTEN: KEY_LISTEN; - - // LEDs - pinMode(LED_WIFI, OUTPUT); // active low - digitalWrite(LED_WIFI, HIGH); - - // Enable amplifier - pinMode(GPIO_PA_EN, OUTPUT); - - // Configure keys on ESP32 Audio Kit board - pinMode(key_listen, INPUT_PULLUP); - // pinMode(KEY_VOL_UP, INPUT_PULLUP); - // pinMode(KEY_VOL_DOWN, INPUT_PULLUP); - - // now initialize read mode - // setReadMode(); + } + if (variant != UNIDENTIFIED) { + Serial.printf("Found audio controller %s at SDA: %d / SCL: %d\n", a1s_pinouts[variant].label, + a1s_pinouts[variant].i2c.sda, a1s_pinouts[variant].i2c.scl); + } else { + Serial.println("No supported controller found, stopping operation!"); + while (true) + ; // TODO: Better handling for no controller found + } + + key_listen = is_es ? ES_KEY_LISTEN : KEY_LISTEN; + + // LEDs + pinMode(LED_WIFI, OUTPUT); // active low + digitalWrite(LED_WIFI, HIGH); + + // Enable amplifier + pinMode(GPIO_PA_EN, OUTPUT); + + // Configure keys on ESP32 Audio Kit board + pinMode(key_listen, INPUT_PULLUP); + // pinMode(KEY_VOL_UP, INPUT_PULLUP); + // pinMode(KEY_VOL_DOWN, INPUT_PULLUP); + + // now initialize read mode + // setReadMode(); }; /** - * @brief called to indicate state of device / service. LED D4 indicates "HOTWORD DETECTED/LISTEN MODE", LED D5 indicates "WIFI DISCONNECT". Different brightness or colors - * are not supported on the AudioKit - * - * @param colors used to indicate state to display, see device.h (enum LedColorState) for values + * @brief called to indicate state of device / service. LED D4 indicates "HOTWORD DETECTED/LISTEN MODE", LED D5 + * indicates "WIFI DISCONNECT". Different brightness or colors are not supported on the AudioKit + * + * @param colors used to indicate state to display, see device.h (enum LedColorState) for values */ void AudioKit::updateColors(StateColors colors) { - switch (colors) - { - case COLORS_HOTWORD: - // very slow pulsing of LED - indicator_light->setPulseTime(4000); - indicator_light->setState(PULSING); - break; - case COLORS_WIFI_CONNECTED: - // quick flashing of LED - indicator_light->setDutyPercent(50); - indicator_light->setPulseTime(1000); - indicator_light->setState(BLINKING); - break; - case COLORS_WIFI_DISCONNECTED: - // slower flashing of LED - indicator_light->setDutyPercent(25); - indicator_light->setPulseTime(2000); - indicator_light->setState(BLINKING); - break; - case COLORS_IDLE: - // all lights are off - indicator_light->setState(OFF); - break; - case COLORS_ERROR: - // very quick blinking of LED - indicator_light->setDutyPercent(25); - indicator_light->setPulseTime(500); - indicator_light->setState(BLINKING); - break; - case COLORS_OTA: - // very quick pulsing of LED - indicator_light->setPulseTime(500); - indicator_light->setState(PULSING); - break; - case COLORS_TTS: - indicator_light->setState(ON); - break; - - } + switch (colors) { + case COLORS_HOTWORD: + // very slow pulsing of LED + indicator_light->setPulseTime(4000); + indicator_light->setState(PULSING); + break; + case COLORS_WIFI_CONNECTED: + // quick flashing of LED + indicator_light->setDutyPercent(50); + indicator_light->setPulseTime(1000); + indicator_light->setState(BLINKING); + break; + case COLORS_WIFI_DISCONNECTED: + // slower flashing of LED + indicator_light->setDutyPercent(25); + indicator_light->setPulseTime(2000); + indicator_light->setState(BLINKING); + break; + case COLORS_IDLE: + // all lights are off + indicator_light->setState(OFF); + break; + case COLORS_ERROR: + // very quick blinking of LED + indicator_light->setDutyPercent(25); + indicator_light->setPulseTime(500); + indicator_light->setState(BLINKING); + break; + case COLORS_OTA: + // very quick pulsing of LED + indicator_light->setPulseTime(500); + indicator_light->setState(PULSING); + break; + case COLORS_TTS: + indicator_light->setState(ON); + break; + } }; void AudioKit::InitI2SSpeakerOrMic(int mode) { - Serial.printf("InitI2SSpeakerOrMic -> %s\n", mode == MODE_MIC ? "Mic" : "Speaker"); - esp_err_t err = ESP_OK; - - i2s_driver_uninstall(SPEAKER_I2S_NUMBER); - i2s_config_t i2s_config = { - .mode = (i2s_mode_t)(I2S_MODE_MASTER), - .sample_rate = 16000, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 16bit, stereo, MSB - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 8, - .dma_buf_len = (((mode == MODE_MIC) ? this->readSize : this->writeSize) * (is_es ? 2 : 1)) / 4, - }; - if (mode == MODE_MIC) - { - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX); - } - else - { - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX); - i2s_config.tx_desc_auto_clear = true; - } - - err += i2s_driver_install(SPEAKER_I2S_NUMBER, &i2s_config, 0, NULL); - - err += i2s_set_pin(SPEAKER_I2S_NUMBER, &getPinConfig()); - - if (is_es) - { - // ES8388Control requires MCLK output. - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - } - return; + Serial.printf("InitI2SSpeakerOrMic -> %s\n", mode == MODE_MIC ? "Mic" : "Speaker"); + esp_err_t err = ESP_OK; + + i2s_driver_uninstall(SPEAKER_I2S_NUMBER); + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER), + .sample_rate = 16000, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // is fixed at 16bit, stereo, MSB + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 8, + .dma_buf_len = (((mode == MODE_MIC) ? this->readSize : this->writeSize) * (is_es ? 2 : 1)) / 4, + }; + if (mode == MODE_MIC) { + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX); + } else { + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX); + i2s_config.tx_desc_auto_clear = true; + } + + err += i2s_driver_install(SPEAKER_I2S_NUMBER, &i2s_config, 0, NULL); + + err += i2s_set_pin(SPEAKER_I2S_NUMBER, &getPinConfig()); + + if (is_es) { + // ES8388Control requires MCLK output. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + WRITE_PERI_REG(PIN_CTRL, 0xFFF0); + } + return; } void AudioKit::setWriteMode(int sampleRate, int bitDepth, int numChannels) { - if (mode != MODE_SPK) - { - InitI2SSpeakerOrMic(MODE_SPK); - mode = MODE_SPK; - } - if (sampleRate > 0) - { - if (is_es) - { - // es8388 requires stereo i2s - i2s_set_clk(SPEAKER_I2S_NUMBER, sampleRate, static_cast(bitDepth), I2S_CHANNEL_STEREO); - } - else - { - i2s_set_clk(SPEAKER_I2S_NUMBER, sampleRate, static_cast(bitDepth), static_cast(numChannels)); - } - // ES8388Control is never put into mono mode, hence we have to handle this case - is_mono_stream_stereo_out = is_es && numChannels == 1; + if (mode != MODE_SPK) { + InitI2SSpeakerOrMic(MODE_SPK); + mode = MODE_SPK; + } + if (sampleRate > 0) { + if (is_es) { + // es8388 requires stereo i2s + i2s_set_clk(SPEAKER_I2S_NUMBER, sampleRate, static_cast(bitDepth), I2S_CHANNEL_STEREO); + } else { + i2s_set_clk(SPEAKER_I2S_NUMBER, sampleRate, static_cast(bitDepth), + static_cast(numChannels)); } + // ES8388Control is never put into mono mode, hence we have to handle this case + is_mono_stream_stereo_out = is_es && numChannels == 1; + } } void AudioKit::setReadMode() { - if (mode != MODE_MIC) - { - InitI2SSpeakerOrMic(MODE_MIC); - - i2s_set_clk(SPEAKER_I2S_NUMBER, 16000, I2S_BITS_PER_SAMPLE_16BIT, is_es ? I2S_CHANNEL_STEREO : I2S_CHANNEL_MONO); + if (mode != MODE_MIC) { + InitI2SSpeakerOrMic(MODE_MIC); - mode = MODE_MIC; - } + i2s_set_clk(SPEAKER_I2S_NUMBER, 16000, I2S_BITS_PER_SAMPLE_16BIT, is_es ? I2S_CHANNEL_STEREO : I2S_CHANNEL_MONO); + + mode = MODE_MIC; + } } void AudioKit::writeAudio(uint8_t *data, size_t size, size_t *bytes_written) { - if (is_mono_stream_stereo_out == false) - { - // input stream and I2S confguration have same number of channels - i2s_write(SPEAKER_I2S_NUMBER, data, size, bytes_written, portMAX_DELAY); - } - else - { - // input stream is mono, I2S needs stereo, hence samples are send - // twice to I2S stream to create 2 channels - // HACK: This works ATM only for 16bit samples as sample size is hardcoded - // here - uint16_t data2[size]; - uint16_t *data1 = (uint16_t *)data; - - for (int idx = 0; idx < size / 2; idx++) - { - data2[2 * idx] = data1[idx]; - data2[2 * idx + 1] = data1[idx]; - - } - - i2s_write(SPEAKER_I2S_NUMBER, data2, 2 * size, bytes_written, portMAX_DELAY); - *bytes_written /= 2; // half the actual bytes written as we have double the stream size + if (is_mono_stream_stereo_out == false) { + // input stream and I2S confguration have same number of channels + i2s_write(SPEAKER_I2S_NUMBER, data, size, bytes_written, portMAX_DELAY); + } else { + // input stream is mono, I2S needs stereo, hence samples are send + // twice to I2S stream to create 2 channels + // HACK: This works ATM only for 16bit samples as sample size is hardcoded + // here + uint16_t data2[size]; + uint16_t *data1 = (uint16_t *)data; + + for (int idx = 0; idx < size / 2; idx++) { + data2[2 * idx] = data1[idx]; + data2[2 * idx + 1] = data1[idx]; } + + i2s_write(SPEAKER_I2S_NUMBER, data2, 2 * size, bytes_written, portMAX_DELAY); + *bytes_written /= 2; // half the actual bytes written as we have double the stream size + } } bool AudioKit::readAudio(uint8_t *data, size_t size) { - size_t byte_read; + size_t byte_read; - if (!is_es) - { - i2s_read(SPEAKER_I2S_NUMBER, data, size, &byte_read, pdMS_TO_TICKS(100)); - } - else - { - // ES8388Control returns stereo stream from Mic, but we need only one channel, - // we drop channel 2 (right channel) here - uint16_t data2[size]; - uint16_t *data1 = (uint16_t *)data; - - i2s_read(SPEAKER_I2S_NUMBER, data2, 2 * size, &byte_read, pdMS_TO_TICKS(100)); - - for (size_t idx = 0; idx < size / 2; idx++) - { - data1[idx] = data2[idx * 2]; - } - byte_read /= 2; + if (!is_es) { + i2s_read(SPEAKER_I2S_NUMBER, data, size, &byte_read, pdMS_TO_TICKS(100)); + } else { + // ES8388Control returns stereo stream from Mic, but we need only one channel, + // we drop channel 2 (right channel) here + uint16_t data2[size]; + uint16_t *data1 = (uint16_t *)data; + + i2s_read(SPEAKER_I2S_NUMBER, data2, 2 * size, &byte_read, pdMS_TO_TICKS(100)); + + for (size_t idx = 0; idx < size / 2; idx++) { + data1[idx] = data2[idx * 2]; } + byte_read /= 2; + } - return byte_read == size; + return byte_read == size; } void AudioKit::muteOutput(bool mute) { - // switching the amp power prevents klicking noise reaching the speaker - if (out_amp != AmpOut::AMP_OUT_HEADPHONE) - { - digitalWrite(GPIO_PA_EN, mute ? LOW : HIGH); - } - - if (out_amp != AmpOut::AMP_OUT_SPEAKERS) - { - if (is_es) - { - // ES8388: we mute main dac - es8388.mute(ES8388Control::ES_MAIN, mute); - } - else - { - // AC101: we just mute the headphone - ac.SetVolumeHeadphone(mute? 0: (out_vol * 63)/100); - } + // switching the amp power prevents klicking noise reaching the speaker + if (out_amp != AmpOut::AMP_OUT_HEADPHONE) { + digitalWrite(GPIO_PA_EN, mute ? LOW : HIGH); + } + + if (out_amp != AmpOut::AMP_OUT_SPEAKERS) { + if (is_es) { + // ES8388: we mute main dac + es8388.mute(ES8388Control::ES_MAIN, mute); + } else { + // AC101: we just mute the headphone + ac.SetVolumeHeadphone(mute ? 0 : (out_vol * 63) / 100); } + } } /** * @brief sets output volume for all outputs - * + * * @param volume 0 - 100 */ void AudioKit::setVolume(uint16_t volume) { - out_vol = volume; + out_vol = volume; - if (is_es) - { - es8388.volume(ES8388Control::ES_MAIN, out_vol); - } - else - { - // volume is 0 to 100, needs to be 0 to 63 - const uint8_t vol = (uint8_t)(out_vol / 100.0f * 63.0f); - - switch(out_amp) - { - case AmpOut::AMP_OUT_SPEAKERS: - ac.SetVolumeSpeaker(vol); - break; - case AmpOut::AMP_OUT_HEADPHONE: - ac.SetVolumeHeadphone(vol); - break; - case AmpOut::AMP_OUT_BOTH: - ac.SetVolumeSpeaker(vol); - ac.SetVolumeHeadphone(vol); - break; - - } + if (is_es) { + es8388.volume(ES8388Control::ES_MAIN, out_vol); + } else { + // volume is 0 to 100, needs to be 0 to 63 + const uint8_t vol = (uint8_t)(out_vol / 100.0f * 63.0f); + + switch (out_amp) { + case AmpOut::AMP_OUT_SPEAKERS: + ac.SetVolumeSpeaker(vol); + break; + case AmpOut::AMP_OUT_HEADPHONE: + ac.SetVolumeHeadphone(vol); + break; + case AmpOut::AMP_OUT_BOTH: + ac.SetVolumeSpeaker(vol); + ac.SetVolumeHeadphone(vol); + break; } + } } /** - * @brief Regularily called to check if hotword is detected. Here no real detection takes + * @brief Regularily called to check if hotword is detected. Here no real detection takes * places, it is simply the press of a button which activates command listening mode. - * - * @return true if listening key is pressed + * + * @return true if listening key is pressed * @return false if listening key is not pressed */ bool AudioKit::isHotwordDetected() { - // TODOD debounce maybe? - return digitalRead(key_listen) == LOW; + // TODOD debounce maybe? + return digitalRead(key_listen) == LOW; } void AudioKit::ampOutput(int ampOut) { - out_amp = (AmpOut)ampOut; - - // spk, headphone - bool mute[2] = {false, false}; - - switch (out_amp) - { - case AmpOut::AMP_OUT_SPEAKERS: - mute[0] = false; - mute[1] = true; - break; - - case AmpOut::AMP_OUT_HEADPHONE: - mute[0] = true; - mute[1] = false; - break; - default: - mute[0] = false; - mute[1] = false; - } - - digitalWrite(GPIO_PA_EN, mute[0] ? LOW : HIGH); - - if (is_es) - { - es8388.mute(ES8388Control::ES_OUT2, mute[1]); - es8388.volume(ES8388Control::ES_OUT2, mute[1] ? 0 : 100); - - es8388.mute(ES8388Control::ES_OUT1, mute[0]); - es8388.volume(ES8388Control::ES_OUT1, mute[0] ? 0 : 100); - } - else - { - const uint8_t vol = (out_vol * 63)/100; - ac.SetVolumeSpeaker(mute[0]? 0 : vol); - ac.SetVolumeHeadphone(mute[1]? 0 : vol); - } + out_amp = (AmpOut)ampOut; + + // spk, headphone + bool mute[2] = {false, false}; + + switch (out_amp) { + case AmpOut::AMP_OUT_SPEAKERS: + mute[0] = false; + mute[1] = true; + break; + + case AmpOut::AMP_OUT_HEADPHONE: + mute[0] = true; + mute[1] = false; + break; + default: + mute[0] = false; + mute[1] = false; + } + + digitalWrite(GPIO_PA_EN, mute[0] ? LOW : HIGH); + + if (is_es) { + es8388.mute(ES8388Control::ES_OUT2, mute[1]); + es8388.volume(ES8388Control::ES_OUT2, mute[1] ? 0 : 100); + + es8388.mute(ES8388Control::ES_OUT1, mute[0]); + es8388.volume(ES8388Control::ES_OUT1, mute[0] ? 0 : 100); + } else { + const uint8_t vol = (out_vol * 63) / 100; + ac.SetVolumeSpeaker(mute[0] ? 0 : vol); + ac.SetVolumeHeadphone(mute[1] ? 0 : vol); + } } void AudioKit::updateBrightness(int brightness) { - indicator_light->setMaxBrightness((brightness*indicator_light->limit)/100); -} \ No newline at end of file + indicator_light->setMaxBrightness((brightness * indicator_light->limit) / 100); +} \ No newline at end of file diff --git a/PlatformIO/src/devices/ES8388Control.cpp b/PlatformIO/src/devices/ES8388Control.cpp index 0839f88..2410ddc 100644 --- a/PlatformIO/src/devices/ES8388Control.cpp +++ b/PlatformIO/src/devices/ES8388Control.cpp @@ -65,179 +65,175 @@ bool ES8388Control::write_reg(uint8_t slave_add, uint8_t reg_add, uint8_t data) { - Wire.beginTransmission(slave_add); - Wire.write(reg_add); - Wire.write(data); - return Wire.endTransmission() == 0; + Wire.beginTransmission(slave_add); + Wire.write(reg_add); + Wire.write(data); + return Wire.endTransmission() == 0; } bool ES8388Control::read_reg(uint8_t slave_add, uint8_t reg_add, uint8_t &data) { - bool retval = false; - Wire.beginTransmission(slave_add); - Wire.write(reg_add); - Wire.endTransmission(false); - Wire.requestFrom((uint16_t)slave_add, (uint8_t)1, true); - if (Wire.available() >= 1) - { - data = Wire.read(); - retval = true; - } - return retval; + bool retval = false; + Wire.beginTransmission(slave_add); + Wire.write(reg_add); + Wire.endTransmission(false); + Wire.requestFrom((uint16_t)slave_add, (uint8_t)1, true); + if (Wire.available() >= 1) { + data = Wire.read(); + retval = true; + } + return retval; } bool ES8388Control::begin(int sda, int scl, uint32_t frequency) { - bool res = identify(sda, scl, frequency); - - if (res == true) - { - - /* mute DAC during setup, power up all systems, slave mode */ - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL3, 0x04); - res &= write_reg(ES8388_ADDR, ES8388_CONTROL2, 0x50); - res &= write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0x00); - res &= write_reg(ES8388_ADDR, ES8388_MASTERMODE, 0x00); - - /* power up DAC and enable LOUT1+2 / ROUT1+2, ADC sample rate = DAC sample rate */ - res &= write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x3e); - res &= write_reg(ES8388_ADDR, ES8388_CONTROL1, 0x12); - - /* DAC I2S setup: 16 bit word length, I2S format; MCLK / Fs = 256*/ - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL1, 0x18); - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL2, 0x02); - - /* DAC to output route mixer configuration: ADC MIX TO OUTPUT */ - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL16, 0x1B); - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL17, 0x90); - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL20, 0x90); - - /* DAC and ADC use same LRCK, enable MCLK input; output resistance setup */ - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x80); - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL23, 0x00); - - /* DAC volume control: 0dB (maximum, unattenuated) */ - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL5, 0x00); - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL4, 0x00); - - /* power down ADC while configuring; volume: +9dB for both channels */ - res &= write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0xff); - res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL1, 0x88); // +24db - - /* select LINPUT2 / RINPUT2 as ADC input; stereo; 16 bit word length, format right-justified, MCLK / Fs = 256 */ - res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL2, 0xf0); // 50 - res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL3, 0x80); // 00 - res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, 0x0e); - res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL5, 0x02); - - /* set ADC volume */ - res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL8, 0x20); - res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL9, 0x20); - - /* set LOUT1 / ROUT1 volume: 0dB (unattenuated) */ - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL24, 0x1e); - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL25, 0x1e); - - /* set LOUT2 / ROUT2 volume: 0dB (unattenuated) */ - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL26, 0x1e); - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL27, 0x1e); - - /* power up and enable DAC; power up ADC (no MIC bias) */ - res &= write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x3c); - res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL3, 0x00); - res &= write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0x00); - } - - return res; + bool res = identify(sda, scl, frequency); + + if (res == true) { + + /* mute DAC during setup, power up all systems, slave mode */ + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL3, 0x04); + res &= write_reg(ES8388_ADDR, ES8388_CONTROL2, 0x50); + res &= write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0x00); + res &= write_reg(ES8388_ADDR, ES8388_MASTERMODE, 0x00); + + /* power up DAC and enable LOUT1+2 / ROUT1+2, ADC sample rate = DAC sample rate */ + res &= write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x3e); + res &= write_reg(ES8388_ADDR, ES8388_CONTROL1, 0x12); + + /* DAC I2S setup: 16 bit word length, I2S format; MCLK / Fs = 256*/ + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL1, 0x18); + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL2, 0x02); + + /* DAC to output route mixer configuration: ADC MIX TO OUTPUT */ + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL16, 0x1B); + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL17, 0x90); + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL20, 0x90); + + /* DAC and ADC use same LRCK, enable MCLK input; output resistance setup */ + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x80); + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL23, 0x00); + + /* DAC volume control: 0dB (maximum, unattenuated) */ + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL5, 0x00); + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL4, 0x00); + + /* power down ADC while configuring; volume: +9dB for both channels */ + res &= write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0xff); + res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL1, 0x88); // +24db + + /* select LINPUT2 / RINPUT2 as ADC input; stereo; 16 bit word length, format right-justified, MCLK / Fs = 256 */ + res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL2, 0xf0); // 50 + res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL3, 0x80); // 00 + res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, 0x0e); + res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL5, 0x02); + + /* set ADC volume */ + res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL8, 0x20); + res &= write_reg(ES8388_ADDR, ES8388_ADCCONTROL9, 0x20); + + /* set LOUT1 / ROUT1 volume: 0dB (unattenuated) */ + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL24, 0x1e); + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL25, 0x1e); + + /* set LOUT2 / ROUT2 volume: 0dB (unattenuated) */ + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL26, 0x1e); + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL27, 0x1e); + + /* power up and enable DAC; power up ADC (no MIC bias) */ + res &= write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x3c); + res &= write_reg(ES8388_ADDR, ES8388_DACCONTROL3, 0x00); + res &= write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0x00); + } + + return res; } /** - * @brief (un)mute one of the two outputs or main dac output of the ES8388 by switching of the output register bits. Does not really mute the selected output, causes an attenuation. - * hence should be used in conjunction with appropriate volume setting. Main dac output mute does mute both outputs - * - * @param out - * @param muted + * @brief (un)mute one of the two outputs or main dac output of the ES8388 by switching of the output register bits. + * Does not really mute the selected output, causes an attenuation. hence should be used in conjunction with appropriate + * volume setting. Main dac output mute does mute both outputs + * + * @param out + * @param muted */ void ES8388Control::mute(const ES8388_OUT out, const bool muted) { - uint8_t reg_addr; - uint8_t mask_mute; - uint8_t mask_val; - - switch (out) - { - case ES_OUT1: - reg_addr = ES8388_DACPOWER; - mask_mute = (3 << 4); - mask_val = muted ? 0 : mask_mute; - break; - case ES_OUT2: - reg_addr = ES8388_DACPOWER; - mask_mute = (3 << 2); - mask_val = muted ? 0 : mask_mute; - break; - case ES_MAIN: - default: - reg_addr = ES8388_DACCONTROL3; - mask_mute = 1 << 2; - mask_val = muted ? mask_mute : 0; - break; - } - - uint8_t reg; - if (read_reg(ES8388_ADDR, reg_addr, reg)) - { - reg = (reg & ~mask_mute) | (mask_val & mask_mute); - write_reg(ES8388_ADDR, reg_addr, reg); - } + uint8_t reg_addr; + uint8_t mask_mute; + uint8_t mask_val; + + switch (out) { + case ES_OUT1: + reg_addr = ES8388_DACPOWER; + mask_mute = (3 << 4); + mask_val = muted ? 0 : mask_mute; + break; + case ES_OUT2: + reg_addr = ES8388_DACPOWER; + mask_mute = (3 << 2); + mask_val = muted ? 0 : mask_mute; + break; + case ES_MAIN: + default: + reg_addr = ES8388_DACCONTROL3; + mask_mute = 1 << 2; + mask_val = muted ? mask_mute : 0; + break; + } + + uint8_t reg; + if (read_reg(ES8388_ADDR, reg_addr, reg)) { + reg = (reg & ~mask_mute) | (mask_val & mask_mute); + write_reg(ES8388_ADDR, reg_addr, reg); + } } /** - * @brief Set volume gain for the main dac, or for one of the two output channels. Final gain = main gain + out channel gain - * + * @brief Set volume gain for the main dac, or for one of the two output channels. Final gain = main gain + out channel + * gain + * * @param out which gain setting to control * @param vol 0-100 (100 is max) */ void ES8388Control::volume(const ES8388_OUT out, const uint8_t vol) { - const uint32_t max_vol = 100; // max input volume value - - const int32_t max_vol_val = out == ES8388_OUT::ES_MAIN ? 96 : 0x21; // max register value for ES8388 out volume - - uint8_t lreg = 0, rreg = 0; - - switch (out) - { - case ES_MAIN: - lreg = ES8388_DACCONTROL4; - rreg = ES8388_DACCONTROL5; - break; - case ES_OUT1: - lreg = ES8388_DACCONTROL24; - rreg = ES8388_DACCONTROL25; - break; - case ES_OUT2: - lreg = ES8388_DACCONTROL26; - rreg = ES8388_DACCONTROL27; - break; - } - - uint8_t vol_val = vol > max_vol ? max_vol_val : (max_vol_val * vol) / max_vol; - - // main dac volume control is reverse scale (lowest value is loudest) - // hence we reverse the calculated value - if (out == ES_MAIN) - { - vol_val = max_vol_val - vol_val; - } - - write_reg(ES8388_ADDR, lreg, vol_val); - write_reg(ES8388_ADDR, rreg, vol_val); + const uint32_t max_vol = 100; // max input volume value + + const int32_t max_vol_val = out == ES8388_OUT::ES_MAIN ? 96 : 0x21; // max register value for ES8388 out volume + + uint8_t lreg = 0, rreg = 0; + + switch (out) { + case ES_MAIN: + lreg = ES8388_DACCONTROL4; + rreg = ES8388_DACCONTROL5; + break; + case ES_OUT1: + lreg = ES8388_DACCONTROL24; + rreg = ES8388_DACCONTROL25; + break; + case ES_OUT2: + lreg = ES8388_DACCONTROL26; + rreg = ES8388_DACCONTROL27; + break; + } + + uint8_t vol_val = vol > max_vol ? max_vol_val : (max_vol_val * vol) / max_vol; + + // main dac volume control is reverse scale (lowest value is loudest) + // hence we reverse the calculated value + if (out == ES_MAIN) { + vol_val = max_vol_val - vol_val; + } + + write_reg(ES8388_ADDR, lreg, vol_val); + write_reg(ES8388_ADDR, rreg, vol_val); } /** - * @brief Test if device with I2C address for ES8388 is connected to the I2C bus - * + * @brief Test if device with I2C address for ES8388 is connected to the I2C bus + * * @param sda which pin to use for I2C SDA * @param scl which pin to use for I2C SCL * @param frequency which frequency to use as I2C bus frequency @@ -246,7 +242,7 @@ void ES8388Control::volume(const ES8388_OUT out, const uint8_t vol) */ bool ES8388Control::identify(int sda, int scl, uint32_t frequency) { - Wire.begin(sda, scl, frequency); - Wire.beginTransmission(ES8388_ADDR); - return Wire.endTransmission() == 0; + Wire.begin(sda, scl, frequency); + Wire.beginTransmission(ES8388_ADDR); + return Wire.endTransmission() == 0; } diff --git a/PlatformIO/src/devices/ES8388Control.h b/PlatformIO/src/devices/ES8388Control.h index b393e42..f617225 100644 --- a/PlatformIO/src/devices/ES8388Control.h +++ b/PlatformIO/src/devices/ES8388Control.h @@ -4,20 +4,20 @@ class ES8388Control { - bool write_reg(uint8_t slave_add, uint8_t reg_add, uint8_t data); - bool read_reg(uint8_t slave_add, uint8_t reg_add, uint8_t &data); - bool identify(int sda, int scl, uint32_t frequency); + bool write_reg(uint8_t slave_add, uint8_t reg_add, uint8_t data); + bool read_reg(uint8_t slave_add, uint8_t reg_add, uint8_t &data); + bool identify(int sda, int scl, uint32_t frequency); public: - bool begin(int sda = -1, int scl = -1, uint32_t frequency = 400000U); + bool begin(int sda = -1, int scl = -1, uint32_t frequency = 400000U); - enum ES8388_OUT - { - ES_MAIN, // this is the DAC output volume (both outputs) - ES_OUT1, // this is the additional gain for OUT1 - ES_OUT2 // this is the additional gain for OUT2 - }; + enum ES8388_OUT + { + ES_MAIN, // this is the DAC output volume (both outputs) + ES_OUT1, // this is the additional gain for OUT1 + ES_OUT2 // this is the additional gain for OUT2 + }; - void mute(const ES8388_OUT out, const bool muted); - void volume(const ES8388_OUT out, const uint8_t vol); + void mute(const ES8388_OUT out, const bool muted); + void volume(const ES8388_OUT out, const uint8_t vol); };