|
5 | 5 | Copyright (C) 2017 Sebastien Decourriere
|
6 | 6 | Based on AudioFileSourceBuffer class from Earle F. Philhower, III
|
7 | 7 |
|
| 8 | + Copyright (C) 2020 Earle F. Philhower, III |
| 9 | + Rewritten for speed and functionality |
| 10 | +
|
8 | 11 | This program is free software: you can redistribute it and/or modify
|
9 | 12 | it under the terms of the GNU General Public License as published by
|
10 | 13 | the Free Software Foundation, either version 3 of the License, or
|
|
26 | 29 |
|
27 | 30 | AudioFileSourceSPIRAMBuffer::AudioFileSourceSPIRAMBuffer(AudioFileSource *source, uint8_t csPin, uint32_t buffSizeBytes)
|
28 | 31 | {
|
29 |
| - Spiram = new ESP8266Spiram(csPin, 40e6); |
30 |
| - Spiram->begin(); |
31 |
| - Spiram->setSeqMode(); |
32 |
| - ramSize = buffSizeBytes; |
33 |
| - writePtr = 0; |
34 |
| - readPtr = 0; |
35 |
| - src = source; |
36 |
| - length = 0; |
37 |
| - filled = false; |
38 |
| - bytesAvailable = 0; |
39 |
| - audioLogger->printf_P(PSTR("SPI RAM buffer size: %u Bytes\n"), ramSize); |
| 32 | + ram.begin(40, csPin); |
| 33 | + ramSize = buffSizeBytes; |
| 34 | + writePtr = 0; |
| 35 | + readPtr = 0; |
| 36 | + filled = false; |
| 37 | + src = source; |
| 38 | + audioLogger->printf_P(PSTR("SPI RAM buffer size: %u Bytes\n"), ramSize); |
40 | 39 | }
|
41 | 40 |
|
42 | 41 | AudioFileSourceSPIRAMBuffer::~AudioFileSourceSPIRAMBuffer()
|
43 | 42 | {
|
| 43 | + ram.end(); |
44 | 44 | }
|
45 | 45 |
|
46 | 46 | bool AudioFileSourceSPIRAMBuffer::seek(int32_t pos, int dir)
|
47 | 47 | {
|
48 |
| - // Invalidate |
49 |
| - readPtr = 0; |
50 |
| - writePtr = 0; |
51 |
| - length = 0; |
52 |
| - return src->seek(pos, dir); |
| 48 | + // Invalidate |
| 49 | + readPtr = 0; |
| 50 | + writePtr = 0; |
| 51 | + filled = false; |
| 52 | + return src->seek(pos, dir); |
53 | 53 | }
|
54 | 54 |
|
55 | 55 | bool AudioFileSourceSPIRAMBuffer::close()
|
56 | 56 | {
|
57 |
| - return src->close(); |
| 57 | + return src->close(); |
58 | 58 | }
|
59 | 59 |
|
60 | 60 | bool AudioFileSourceSPIRAMBuffer::isOpen()
|
61 | 61 | {
|
62 |
| - return src->isOpen(); |
| 62 | + return src->isOpen(); |
63 | 63 | }
|
64 | 64 |
|
65 | 65 | uint32_t AudioFileSourceSPIRAMBuffer::getSize()
|
66 | 66 | {
|
67 |
| - return src->getSize(); |
| 67 | + return src->getSize(); |
68 | 68 | }
|
69 | 69 |
|
70 | 70 | uint32_t AudioFileSourceSPIRAMBuffer::getPos()
|
71 | 71 | {
|
72 |
| - return src->getPos(); |
| 72 | + return src->getPos() - (writePtr - readPtr); |
73 | 73 | }
|
74 | 74 |
|
75 | 75 | uint32_t AudioFileSourceSPIRAMBuffer::read(void *data, uint32_t len)
|
76 | 76 | {
|
77 |
| - uint32_t bytes = 0; |
78 |
| - // Check if the buffer isn't empty, otherwise we try to fill completely |
79 |
| - if (!filled) { |
80 |
| - uint8_t buffer[256]; |
81 |
| - writePtr = readPtr = 0; |
82 |
| - uint16_t toRead = sizeof(buffer); |
83 |
| - // Fill up completely before returning any data at all |
84 |
| - audioLogger->printf_P(PSTR("Buffering...\n")); |
85 |
| - while (bytesAvailable!=ramSize) { |
86 |
| - length = src->read(buffer, toRead); |
87 |
| - if(length>0) { |
88 |
| - Spiram->write(writePtr, buffer, length); |
89 |
| - bytesAvailable+=length; |
90 |
| - writePtr = bytesAvailable % ramSize; |
91 |
| - if ((ramSize-bytesAvailable)<toRead) { |
92 |
| - toRead=ramSize-bytesAvailable; |
93 |
| - } |
94 |
| - } else { |
95 |
| - // EOF, break out of read loop |
96 |
| - break; |
97 |
| - } |
98 |
| - } |
99 |
| - writePtr = bytesAvailable % ramSize; |
100 |
| - filled = true; |
101 |
| - audioLogger->printf_P(PSTR("Filling Done !\n")); |
102 |
| - } |
103 |
| - |
104 |
| -// audioLogger->printf("Buffer: %u%\n", bytesAvailable*100/ramSize); |
105 |
| - |
106 |
| - uint8_t *ptr = reinterpret_cast<uint8_t*>(data); |
107 |
| - uint32_t toReadFromBuffer = (len < bytesAvailable) ? len : bytesAvailable; |
108 |
| - if (toReadFromBuffer>0) { |
109 |
| - // Pull from buffer until we've got none left or we've satisfied the request |
110 |
| - Spiram->read(readPtr, ptr, toReadFromBuffer); |
111 |
| - readPtr = (readPtr+toReadFromBuffer) % ramSize; |
112 |
| - bytes = toReadFromBuffer; |
113 |
| - bytesAvailable-=toReadFromBuffer; |
114 |
| - len-=toReadFromBuffer; |
115 |
| - ptr += toReadFromBuffer; |
116 |
| - } |
117 |
| - |
118 |
| - // If len>O there is no data left in buffer and we try to read more directly from source. |
119 |
| - // Then, we trigger a complete buffer refill |
120 |
| - if (len) { |
121 |
| - bytes += src->read(ptr, len); |
122 |
| - bytesAvailable = 0; |
123 |
| - filled = false; |
124 |
| - } |
125 |
| - return bytes; |
| 77 | + uint32_t bytes = 0; |
| 78 | + |
| 79 | + // Check if the buffer isn't empty, otherwise we try to fill completely |
| 80 | + if (!filled) { |
| 81 | + cb.st(999, PSTR("Filling buffer...")); |
| 82 | + uint8_t buffer[256]; |
| 83 | + writePtr = 0; |
| 84 | + readPtr = 0; |
| 85 | + // Fill up completely before returning any data at all |
| 86 | + do { |
| 87 | + int toRead = std::min(ramSize - (writePtr - readPtr), sizeof(buffer)); |
| 88 | + int length = src->read(buffer, toRead); |
| 89 | + if (length > 0) { |
| 90 | +#ifdef FAKERAM |
| 91 | + for (size_t i=0; i<length; i++) fakeRAM[(i+writePtr)%ramSize] = buffer[i]; |
| 92 | +#else |
| 93 | + ram.writeBytes(writePtr % ramSize, buffer, length); |
| 94 | +#endif |
| 95 | + writePtr += length; |
| 96 | + } else { |
| 97 | + // EOF, break out of read loop |
| 98 | + break; |
| 99 | + } |
| 100 | + } while ((writePtr - readPtr) < ramSize); |
| 101 | + filled = true; |
| 102 | + cb.st(999, PSTR("Buffer filled...")); |
| 103 | + } |
| 104 | + |
| 105 | + // Read up to the entire buffer from RAM |
| 106 | + uint32_t toReadFromBuffer = std::min(len, writePtr - readPtr); |
| 107 | + uint8_t *ptr = reinterpret_cast<uint8_t*>(data); |
| 108 | + if (toReadFromBuffer > 0) { |
| 109 | +#ifdef FAKERAM |
| 110 | + for (size_t i=0; i<toReadFromBuffer; i++) ptr[i] = fakeRAM[(i+readPtr)%ramSize]; |
| 111 | +#else |
| 112 | + ram.readBytes(readPtr % ramSize, ptr, toReadFromBuffer); |
| 113 | +#endif |
| 114 | + readPtr += toReadFromBuffer; |
| 115 | + ptr += toReadFromBuffer; |
| 116 | + bytes += toReadFromBuffer; |
| 117 | + len -= toReadFromBuffer; |
| 118 | + } |
| 119 | + |
| 120 | + // If len>0 there is no data left in buffer and we try to read more directly from source. |
| 121 | + // Then, we trigger a complete buffer refill |
| 122 | + if (len) { |
| 123 | + bytes += src->read(data, len); |
| 124 | + filled = false; |
| 125 | + } |
| 126 | + return bytes; |
126 | 127 | }
|
127 | 128 |
|
128 | 129 | void AudioFileSourceSPIRAMBuffer::fill()
|
129 | 130 | {
|
130 |
| - // Make sure the buffer is pre-filled before make partial fill. |
131 |
| - if (!filled) return; |
132 |
| - |
133 |
| - // Now trying to refill SPI RAM Buffer |
134 |
| - uint8_t buffer[128]; |
135 |
| - // Make sure there is at least buffer size free in RAM |
136 |
| - if ((ramSize - bytesAvailable)<sizeof(buffer)) { |
137 |
| - return; |
138 |
| - } |
139 |
| - uint16_t cnt = src->readNonBlock(buffer, sizeof(buffer)); |
140 |
| - if (cnt) { |
141 |
| - Spiram->write(writePtr, buffer, cnt); |
142 |
| - bytesAvailable+=cnt; |
143 |
| - writePtr = (writePtr + cnt) % ramSize; |
144 |
| -#ifdef SPIBUF_DEBUG |
145 |
| - audioLogger->printf_P(PSTR("SockRead: %u | RamAvail: %u\n"), cnt, bytesAvailable); |
| 131 | + // Make sure the buffer is pre-filled before make partial fill. |
| 132 | + if (!filled) return; |
| 133 | + |
| 134 | + for (auto i=0; i<5; i++) { |
| 135 | + // Make sure there is at least buffer size free in RAM |
| 136 | + uint8_t buffer[128]; |
| 137 | + if ((ramSize - (writePtr - readPtr)) < sizeof(buffer)) { |
| 138 | + return; |
| 139 | + } |
| 140 | + |
| 141 | + int cnt = src->readNonBlock(buffer, sizeof(buffer)); |
| 142 | + if (cnt) { |
| 143 | +#ifdef FAKERAM |
| 144 | + for (size_t i=0; i<cnt; i++) fakeRAM[(i+writePtr)%ramSize] = buffer[i]; |
| 145 | +#else |
| 146 | + ram.writeBytes(writePtr % ramSize, buffer, cnt); |
146 | 147 | #endif
|
147 |
| - } |
148 |
| - return; |
| 148 | + writePtr += cnt; |
| 149 | + } |
| 150 | + } |
149 | 151 | }
|
150 | 152 |
|
151 | 153 | bool AudioFileSourceSPIRAMBuffer::loop()
|
152 | 154 | {
|
153 |
| - if (!src->loop()) return false; |
154 |
| - fill(); |
155 |
| - return true; |
| 155 | + static uint32_t last = 0; |
| 156 | + if (!src->loop()) return false; |
| 157 | + fill(); |
| 158 | + if ((ESP.getCycleCount() - last) > microsecondsToClockCycles(1000000)) { |
| 159 | + last = ESP.getCycleCount(); |
| 160 | + char str[65]; |
| 161 | + memset(str, '#', 64); |
| 162 | + str[64] = 0; |
| 163 | + str[((writePtr - readPtr) * 64)/ramSize] = 0; |
| 164 | + cb.st(((writePtr - readPtr) * 100)/ramSize, str); |
| 165 | + } |
| 166 | + return true; |
156 | 167 | }
|
0 commit comments