#ifndef VS1003_H #define VS1003_H #include "../../io/fastGPIO.h" #include "../../io/SoftSPI.h" #include "../../Debug.h" //#include "IPlayable.h" //#include "AudioData.h" #include "../../data/ChunkBuffer.h" /** * control a VS1003 audio chip attached to the SPI port */ template class VS1003 { private: static constexpr const char* NAME = "VS1003"; //SoftSPI spi; #define SPI_INIT() spi::init(); #define SPI_WRITE_8(byte) spi::writeByte(byte) #define SPI_WRITE_16(word) spi::writeWord(word) #define SPI_READ_8(byte) spi::readByte(byte) #define SPI_READ_16(word) spi::readWord(word) #define SPI_READ_WRITE_8(byte) spi::readWriteByte(byte) #define SPI_READ_WRITE_16(word) spi::readWriteWord(word) //#define SPI_DELAY() os_delay_us(10) #define SPI_DELAY() //os_delay_us(10) #define GET_DREQ() GPIO16_IN // PIN D0 #define MP3_CTRL_DESELECT() spi::chipDeselect(); #define MP3_CTRL_SELECT() spi::chipSelect(); #define MP3_DATA_DESELECT() GPIO5_H #define MP3_DATA_SELECT() GPIO5_L enum OP { WRITE = 0x2, READ = 0x3, }; enum Register { SCI_MODE = 0x0, SCI_STATUS = 0x1, SCI_BASS = 0x2, SCI_CLOCKF = 0x3, SCI_DECODE_TIME = 0x4, SCI_AUDATA = 0x5, SCI_WRAM = 0x6, SCI_WRAMADDR = 0x7, SCI_HDAT0 = 0x8, SCI_HDAT1 = 0x9, SCI_AIADDR = 0xa, SCI_VOL = 0xb, SCI_AICTRL0 = 0xc, SCI_AICTRL1 = 0xd, SCI_AICTRL2 = 0xe, SCI_AICTRL3 = 0xf, }; /** * @brief Bitfield-combinable enum with all mode-flags that can be set for the VS1003. */ enum class Mode { NONE = 0, SM_DIFF = 1, SM_SETTOZERO = 2, SM_RESET = 4, SM_OUTOFWAV = 8, SM_POWERDOWN = 16, SM_TESTS = 32, SM_STREAM = 64, SM_SETTOZERO2 = 128, SM_DACT = 256, SM_SDIORD = 512, SM_SDISHARE = 1024, SM_SDINEW = 2048, SM_ADPCM = 4096, SM_ADCPM_HP = 8192, SM_LINE_IN = 16384, }; public: enum class RecordSource { MICROPHONE, LINE_IN }; enum class PlayState { STOPPED, PLAYING, RECORDING }; public: VS1003() { init(); } /** initialize the hardware */ void init() { debugMod(NAME, "init()"); SPI_INIT(); GPIO5_OUTPUT_SET; // Pin D1 - XDCS GPIO16_INPUT_SET; // Pin D0 - DREQ this->state = PlayState::STOPPED; awaitDREQ(); // Wait for the VS1003 to finish boot reset(); } /** * @brief Set audio volume. * @param Audio volume to set on both channels. */ void setVolume(const uint8_t vol) const { debugMod1(NAME, "setVolume(%d)", vol); const uint8_t aVol = 255 - vol; const uint16_t values = (aVol << 0) | (aVol << 8); // left and right writeRegister(SCI_VOL, values); } uint8_t getVolume() const { const uint16_t values = readRegister(SCI_VOL); return (255 - (values & 0xFF)); } // void play(AudioData ad) { // this->playable = ad; // this->state = PlayState::PLAYING; // } // void appendUnmanaged(const uint8_t* data, const uint16_t size) { // ChunkBuffer::Chunk* c = playBuf.wrap(data, size); // playBuf.append(c); // this->state = PlayState::PLAYING; // } void ICACHE_FLASH_ATTR record(RecordSource source) { state = PlayState::RECORDING; writeRegister(Register::SCI_CLOCKF, 0x4430); /* 2.0x 12.288MHz */ os_delay_us(100); writeRegister(Register::SCI_AICTRL0, 12); /* Div -> 12=8kHz 8=12kHz 6=16kHz */ os_delay_us(100); writeRegister(Register::SCI_AICTRL1, 0); /* Auto gain */ os_delay_us(100); setVolume(255); os_printf("mode: %d", readRegister(Register::SCI_MODE)); writeRegister(Register::SCI_MODE, readRegister(Register::SCI_MODE) | (int)Mode::SM_ADPCM | (int)Mode::SM_RESET); os_delay_us(100); os_printf("mode: %d", readRegister(Register::SCI_MODE)); // if(source == RecordSource::LINE_IN) { // reset((Mode)((int)Mode::SM_LINE_IN | (int)Mode::SM_ADPCM)); // } else { // reset(Mode::SM_ADPCM); // } awaitDREQ(); os_delay_us(100); } PlayState ICACHE_FLASH_ATTR getState() const { return this->state; } void play() { //if(this->state == PlayState::STOPPED) { reset(); } // makes things more stable this->state = PlayState::PLAYING; } void ICACHE_FLASH_ATTR run() { if(this->state == PlayState::PLAYING) { if (playBuf.empty()) { //uint8_t stopSequence[4] = {0}; //playChunk32(stopSequence, sizeof(stopSequence)); // end of stream //this->state = PlayState::STOPPED; } else { //const ChunkBuffer::Data chunk = playBuf.get(32); //playChunk32(chunk.ptr, chunk.size); sendPlayBuffer(); } } } uint32_t getRecordData(uint8_t* buffer, uint32_t maxLength) { uint32_t available = this->getNumAvailableRecordBytes(); // os_printf("avail: %d\r\n", available); if (available < 256 || available >= 896) {return 0;} int toRead = 256; int read = 0; while(read != toRead) { const uint16_t data = readRegister(Register::SCI_HDAT0); buffer[read] = (data >> 8); ++read; buffer[read] = (data & 0xFF); ++read; //os_delay_us(10); } return toRead; // for (int i = 0; i < 4; ++i) { // os_printf("%d ", buffer[i]); // } // os_printf("\r\n"); // uint32_t len = min(maxLength, available); // for(int i = 0; i < len; ++i) { // uint16_t data = readRegister(Register::SCI_HDAT0); // buffer[i++] = (data >> 8); // buffer[i] = (data & 0xFF); // os_delay_us(5); // } } PlayBuf& getPlayBuffer() { return playBuf; } void stop() { this->state = PlayState::STOPPED; playBuf.clear(); //reset(); } private: uint32_t getNumAvailableRecordBytes() { return (uint32_t)readRegister(Register::SCI_HDAT1) * sizeof(uint16_t); } void ICACHE_FLASH_ATTR playChunk32(const uint8_t* buffer, const uint32_t len) const { debugMod1(NAME, "playChunk32() len:%d", len); MP3_CTRL_DESELECT(); MP3_DATA_SELECT(); for(uint32_t i = 0; i < len; ++i) { awaitDREQ(); SPI_WRITE_8(buffer[i]); } MP3_DATA_DESELECT(); } void ICACHE_FLASH_ATTR sendPlayBuffer() { MP3_CTRL_DESELECT(); MP3_DATA_SELECT(); int written = 0; while(canConsume() && !playBuf.empty()) { const uint8_t byte = playBuf.get(); if (byte == -1) {break;} SPI_WRITE_8(byte); ++written; } if (written) {debugMod1(NAME, "sendPlayBuffer(): %d bytes", written);} MP3_DATA_DESELECT(); } /** * @brief Write the given register. * @param reg Register to write to. * @param value Value to write into the given register. */ void writeRegister(const Register reg, const uint16_t value) const { MP3_DATA_DESELECT(); MP3_CTRL_SELECT(); SPI_DELAY(); SPI_WRITE_8(OP::WRITE); SPI_WRITE_8(reg); SPI_WRITE_16(value); // hi-byte -> low byte SPI_DELAY(); awaitDREQ(); MP3_CTRL_DESELECT(); } /** * @brief Read the given register. * @param reg Register to read. * @return The value of the register with the given address. */ uint16_t readRegister(const Register reg) const { MP3_DATA_DESELECT(); MP3_CTRL_SELECT(); SPI_DELAY(); SPI_WRITE_8(OP::READ); SPI_WRITE_8(reg); const uint16_t res = SPI_READ_16(); SPI_DELAY(); awaitDREQ(); MP3_CTRL_DESELECT(); return res; } /** * @brief Wait for the VS1003 to set DREQ to high (it wants data). */ inline void awaitDREQ() const { debugMod(NAME, "awaitDREQ()"); while(!GET_DREQ()) {;} //debugMod(NAME, "awaitDREQ() complete"); } /** * @brief VS1003 is able do consume data * @return */ inline bool canConsume() const { return GET_DREQ(); } /** * @brief Reset the VS1003 chip. * @param modeMask Additional mask to or into the mode that is written to the VS1003's register. */ void reset(Mode modeMask = Mode::NONE) const { debugMod(NAME, "reset()"); writeRegister(SCI_MODE, (2052 | (uint16_t)modeMask)); // TODO: remove SM_TESTS os_delay_us(1); awaitDREQ(); writeRegister(SCI_CLOCKF, 0x9800); setVolume(0); os_delay_us(1); MP3_DATA_SELECT(); SPI_WRITE_8(0); SPI_WRITE_8(0); SPI_WRITE_8(0); SPI_WRITE_8(0); MP3_DATA_DESELECT(); os_delay_us(100); MP3_CTRL_DESELECT(); debugMod(NAME, "reset() complete"); } private: PlayState state = PlayState::STOPPED; PlayBuf playBuf; }; #endif // VS1003_H