dspMultiTI.cpp (7219B)
1 #include "dspMultiTI.h" 2 3 namespace virusLib 4 { 5 constexpr uint32_t g_esai1TxBlockSize = 6 * 3 * 2; // 6 = number of TX pins, 3 = number of slots per frame, 2 = double data rate 6 constexpr uint32_t g_esai1TxPadding = g_esai1TxBlockSize; 7 8 constexpr uint32_t g_esai1RxBlockSize = 4 * 3 * 2; // 4 = number of RX pins, 3 = number of slots per frame, 2 = double data rate 9 constexpr uint32_t g_esai1RxPadding = g_esai1RxBlockSize; 10 11 constexpr uint32_t g_magicIntervalSamples = 256; 12 13 static constexpr dsp56k::TWord g_magicNumber = 0xedc987; 14 15 void audioAdd(float& _dst, float _src) 16 { 17 _dst += _src; 18 } 19 20 void audioAdd(dsp56k::TWord& _dst, const dsp56k::TWord _src) 21 { 22 auto dst = dsp56k::signextend<int32_t, 24>(static_cast<int32_t>(_dst)); 23 dst += dsp56k::signextend<int32_t, 24>(static_cast<int32_t>(_src)); 24 _dst = static_cast<dsp56k::TWord>(dst) & 0xffffff; 25 } 26 27 template <typename T> void ensureSize(std::vector<T>& _buf, size_t _size) 28 { 29 if(_buf.size() > _size) 30 return; 31 _buf.resize(_size, 0); 32 } 33 34 template <typename T> void wrapPadding(std::vector<T>& _buf, const uint32_t _usedSize) 35 { 36 size_t iSrc = _usedSize; 37 38 for (size_t i=0; i<g_esai1TxPadding; ++i, ++iSrc) 39 _buf[i] = _buf[iSrc]; 40 } 41 42 template <typename T> void DspMultiTI::Esai1Out::processAudioOutput(dsp56k::Esai& _esai, uint32_t _frames, const synthLib::TAudioOutputsT<T>& _outputs, uint32_t _firstOutChannel, const std::array<uint32_t, 6>& _sourceIndices) 43 { 44 ensureSize(*this, _frames * g_esai1TxBlockSize + g_esai1TxPadding); 45 46 _esai.processAudioOutput(data() + g_esai1TxPadding, _frames * 2); 47 48 if(m_blockStart == InvalidOffset) 49 { 50 for(uint32_t i=g_esai1TxPadding; i<g_esai1TxBlockSize * _frames + g_esai1TxPadding; ++i) 51 { 52 if(at(i) != g_magicNumber) 53 continue; 54 55 // OS writes the magic value to position $b of its internal ring buffer 56 const auto off = i - 0xb; 57 58 m_blockStart = off / g_esai1TxBlockSize; 59 m_blockStart = off - m_blockStart * g_esai1TxBlockSize; 60 break; 61 } 62 } 63 64 if(m_blockStart != InvalidOffset) 65 { 66 const auto* p = &at(m_blockStart); 67 68 for(size_t i=0; i<_frames; ++i) 69 { 70 _outputs[_firstOutChannel ][i] += dsp56k::dsp2sample<T>(p[_sourceIndices[0]]); 71 _outputs[_firstOutChannel+1][i] += dsp56k::dsp2sample<T>(p[_sourceIndices[1]]); 72 _outputs[_firstOutChannel+2][i] += dsp56k::dsp2sample<T>(p[_sourceIndices[2]]); 73 _outputs[_firstOutChannel+3][i] += dsp56k::dsp2sample<T>(p[_sourceIndices[3]]); 74 _outputs[_firstOutChannel+4][i] += dsp56k::dsp2sample<T>(p[_sourceIndices[4]]); 75 _outputs[_firstOutChannel+5][i] += dsp56k::dsp2sample<T>(p[_sourceIndices[5]]); 76 77 p += g_esai1TxBlockSize; 78 } 79 } 80 81 wrapPadding(*this, _frames * g_esai1TxBlockSize); 82 } 83 84 template <typename T> void DspMultiTI::Esai1in::processAudioinput(dsp56k::Esai& _esai, uint32_t _frames, uint32_t _latency, const synthLib::TAudioInputsT<T>& _inputs) 85 { 86 ensureSize(*this, _frames * g_esai1RxBlockSize); 87 88 uint32_t blockIdx = 0; 89 90 static volatile uint32_t offset = 1; 91 92 for(uint32_t i=0; i<_frames; ++i) 93 { 94 at(blockIdx + offset + (g_esai1RxBlockSize>>1)) = dsp56k::sample2dsp<T>(_inputs[0][i]); 95 at(blockIdx + offset ) = dsp56k::sample2dsp<T>(_inputs[1][i]); 96 97 blockIdx += g_esai1RxBlockSize; 98 } 99 100 _esai.processAudioInput(data(), _frames * 2, 3, _latency * 2); 101 } 102 103 DspMultiTI::DspMultiTI() : DspSingle(0x100000, true, "DSP A"), m_dsp2(0x100000, true, "DSP B") 104 { 105 getHDI08().writeHDR(0x0000); // this = Master 106 m_dsp2.getHDI08().writeHDR(0x8000); // m_dsp2 = Slave 107 108 getPeriphX().getEsai().writeEmptyAudioIn(2); 109 m_dsp2.getPeriphX().getEsai().writeEmptyAudioIn(2); 110 } 111 112 template <typename T> 113 void processAudioTI(DspMultiTI& _dsp, DspSingle& _dsp2, DspMultiTI::EsaiBufs<T>& _buffers, const synthLib::TAudioInputsT<T>& _inputs, synthLib::TAudioOutputsT<T> _outputs, const size_t _samples, const uint32_t _latency) 114 { 115 const auto s = static_cast<uint32_t>(_samples); 116 117 // ESAI inputs 118 ensureSize(_buffers.dummyInput, _samples << 1); 119 const T* in = _buffers.dummyInput.data(); 120 const T* inputs[8] = { in, in, in, in, in, in, in, in }; 121 122 // Master ESAI input might be the USB input, we don't need it as we only have one input 123 _dsp.getPeriphX().getEsai().processAudioInputInterleaved(inputs, s, _latency); 124 125 // Master ESAI_1 input gets the analog input from the slave, inject the interleaved input here 126 _buffers.in.processAudioinput(_dsp.getPeriphY().getEsai(), s, _latency, _inputs); 127 128 // Slave ESAI input gets the analog input in regular fashion 129 130 _dsp2.getPeriphX().getEsai().processAudioInput<T>(1, _latency, [&](size_t _s, dsp56k::Audio::RxFrame& _frame) 131 { 132 _frame.resize(2); 133 _frame[0][0] = dsp56k::sample2dsp<T>(_inputs[0][0]); 134 _frame[1][0] = dsp56k::sample2dsp<T>(_buffers.m_previousInput); 135 }); 136 137 _dsp2.getPeriphX().getEsai().processAudioInput<T>(s - 1, _latency, [&](size_t _s, dsp56k::Audio::RxFrame& _frame) 138 { 139 _frame.resize(2); 140 _frame[0][0] = dsp56k::sample2dsp<T>(_inputs[0][_s]); 141 _frame[1][0] = dsp56k::sample2dsp<T>(_inputs[1][_s+1]); 142 }); 143 144 _buffers.m_previousInput = _inputs[1][s-1]; 145 146 // Slave ESAI_1 does not get the ADC at all but only data from the master, we don't need it here 147 _dsp2.getPeriphY().getEsai().processAudioInputInterleaved(inputs, s * 2, _latency * 2); 148 149 // ESAI outputs 150 ensureSize(_buffers.dummyOutput, _samples << 1); 151 for (auto& o : _outputs) 152 { 153 if(!o) 154 o = _buffers.dummyOutput.data(); 155 } 156 157 T* out = _buffers.dummyOutput.data(); 158 T* outputs[12] = {out, out, out, out, out, out, out, out, out, out, out, out}; 159 160 // DAC outputs on the Master are sent in regular fashion 161 outputs[4] = _outputs[0]; outputs[5] = _outputs[1]; 162 outputs[6] = _outputs[2]; outputs[7] = _outputs[3]; 163 outputs[8] = _outputs[4]; outputs[9] = _outputs[5]; 164 165 _dsp.getPeriphX().getEsai().processAudioOutputInterleaved(outputs, s); 166 167 // USB outputs on the Slave are sent in regular fashion 168 outputs[4] = _outputs[6]; outputs[5] = _outputs[7]; 169 outputs[6] = _outputs[8]; outputs[7] = _outputs[9]; 170 outputs[8] = _outputs[10]; outputs[9] = _outputs[11]; 171 172 _dsp2.getPeriphX().getEsai().processAudioOutputInterleaved(outputs, s); 173 174 // ESAI_1 outputs 175 constexpr auto halfBS = g_esai1TxBlockSize >> 1; 176 177 // USB outputs on the Master are sent to the Slave via ESAI_1 in 1/3 interleaved format => unpack it 178 _buffers.dspA.processAudioOutput(_dsp.getPeriphY().getEsai(), s, _outputs, 6, { 179 5, 5+halfBS, 180 16+halfBS, 16, 181 17+halfBS, 17 182 }); 183 184 // DAC outputs on the Slave are send via ESAI_1 to the Master in 1/3 interleaved format => unpack it 185 _buffers.dspB.processAudioOutput(_dsp2.getPeriphY().getEsai(), s, _outputs, 0, { 186 4,4+halfBS, 187 5,5+halfBS, 188 16+halfBS,16 189 }); 190 } 191 192 void DspMultiTI::processAudio(const synthLib::TAudioInputs& _inputs, const synthLib::TAudioOutputs& _outputs, size_t _samples, uint32_t _latency) 193 { 194 processAudioTI(*this, m_dsp2, m_bufferF, _inputs, _outputs, _samples, _latency); 195 } 196 197 void DspMultiTI::processAudio(const synthLib::TAudioInputsInt& _inputs, const synthLib::TAudioOutputsInt& _outputs, size_t _samples, uint32_t _latency) 198 { 199 processAudioTI(*this, m_dsp2, m_bufferI, _inputs, _outputs, _samples, _latency); 200 } 201 }