wavWriter.cpp (5101B)
1 #include "wavWriter.h" 2 3 #include "dsp56kEmu/logging.h" 4 5 #include <map> 6 #include <cassert> 7 #include <chrono> 8 #include <mutex> 9 #include <thread> 10 11 #include "dsp56kEmu/threadtools.h" 12 #include "dsp56kEmu/types.h" 13 14 namespace synthLib 15 { 16 bool WavWriter::write(const std::string & _filename, const int _bitsPerSample, const bool _isFloat, const int _channelCount, const int _samplerate, const void* _data, const size_t _dataSize) 17 { 18 FILE* handle = fopen(_filename.c_str(), m_existingDataSize > 0 ? "rb+" : "wb"); 19 20 if (!handle) 21 { 22 LOG("Failed to open file for writing: " << _filename); 23 return false; 24 } 25 26 fseek(handle, 0, SEEK_END); 27 const auto currentSize = ftell(handle); 28 const bool append = currentSize > 0; 29 30 if(append) 31 { 32 fwrite(_data, 1, _dataSize, handle); 33 fseek(handle, 0, SEEK_SET); 34 const auto pos = ftell(handle); 35 assert(pos == 0); 36 } 37 38 m_existingDataSize += _dataSize; 39 40 SWaveFormatHeader header{}; 41 SWaveFormatChunkInfo chunkInfo{}; 42 SWaveFormatChunkFormat fmt{}; 43 44 header.str_riff[0] = 'R'; 45 header.str_riff[1] = 'I'; 46 header.str_riff[2] = 'F'; 47 header.str_riff[3] = 'F'; 48 49 header.str_wave[0] = 'W'; 50 header.str_wave[1] = 'A'; 51 header.str_wave[2] = 'V'; 52 header.str_wave[3] = 'E'; 53 54 const size_t dataSize = m_existingDataSize; 55 56 header.file_size = static_cast<uint32_t>(sizeof(SWaveFormatHeader) + 57 sizeof(SWaveFormatChunkInfo) + 58 sizeof(SWaveFormatChunkFormat) + 59 sizeof(SWaveFormatChunkInfo) + 60 dataSize - 8); 61 62 fwrite(&header, 1, sizeof(header), handle); 63 const auto pos = ftell(handle); 64 assert(pos == sizeof(header)); 65 66 // write format 67 chunkInfo.chunkName[0] = 'f'; 68 chunkInfo.chunkName[1] = 'm'; 69 chunkInfo.chunkName[2] = 't'; 70 chunkInfo.chunkName[3] = ' '; 71 72 chunkInfo.chunkSize = sizeof(SWaveFormatChunkFormat); 73 74 fwrite(&chunkInfo, 1, sizeof(chunkInfo), handle); 75 76 const auto bytesPerSample = _bitsPerSample >> 3; 77 const auto bytesPerFrame = bytesPerSample * _channelCount; 78 79 fmt.bits_per_sample = _bitsPerSample; 80 fmt.block_alignment = bytesPerFrame; 81 fmt.bytes_per_sec = _samplerate * bytesPerFrame; 82 fmt.num_channels = static_cast<uint16_t>(_channelCount); 83 fmt.sample_rate = static_cast<uint32_t>(_samplerate); 84 fmt.wave_type = _isFloat ? eFormat_IEEE_FLOAT : eFormat_PCM; 85 86 fwrite(&fmt, 1, sizeof(fmt), handle); 87 88 // write data 89 chunkInfo.chunkName[0] = 'd'; 90 chunkInfo.chunkName[1] = 'a'; 91 chunkInfo.chunkName[2] = 't'; 92 chunkInfo.chunkName[3] = 'a'; 93 94 chunkInfo.chunkSize = static_cast<unsigned int>(m_existingDataSize); 95 96 fwrite(&chunkInfo, 1, sizeof(chunkInfo), handle); 97 98 if(!append) 99 fwrite(_data, 1, m_existingDataSize, handle); 100 101 fclose(handle); 102 103 return true; 104 } 105 106 void WavWriter::writeWord(std::vector<uint8_t>& _dst, dsp56k::TWord _word) 107 { 108 const auto d = reinterpret_cast<const uint8_t*>(&_word); 109 _dst.push_back(d[0]); 110 _dst.push_back(d[1]); 111 _dst.push_back(d[2]); 112 } 113 114 AsyncWriter::AsyncWriter(std::string _filename, uint32_t _samplerate, bool _measureSilence) 115 : m_filename(std::move(_filename)) 116 , m_samplerate(_samplerate) 117 , m_measureSilence(_measureSilence) 118 { 119 m_thread.reset(new std::thread([&]() 120 { 121 threadWriteFunc(); 122 })); 123 } 124 125 AsyncWriter::~AsyncWriter() 126 { 127 m_finished = true; 128 129 if(m_thread) 130 { 131 m_thread->join(); 132 m_thread.reset(); 133 } 134 } 135 136 void AsyncWriter::append(const std::function<void(std::vector<dsp56k::TWord>&)>& _func) 137 { 138 std::lock_guard lock(m_writeMutex); 139 _func(m_stereoOutput); 140 } 141 142 void AsyncWriter::threadWriteFunc() 143 { 144 dsp56k::ThreadTools::setCurrentThreadName("AsyncWavWriter"); 145 146 synthLib::WavWriter writer; 147 148 std::vector<dsp56k::TWord> m_wordBuffer; 149 std::vector<uint8_t> m_byteBuffer; 150 m_byteBuffer.reserve(m_wordBuffer.capacity() * 3); 151 152 bool foundNonSilence = false; 153 154 while(true) 155 { 156 { 157 std::lock_guard lock(m_writeMutex); 158 std::swap(m_wordBuffer, m_stereoOutput); 159 } 160 161 if(m_wordBuffer.empty() && m_byteBuffer.empty() && m_finished) 162 break; 163 164 if(!m_wordBuffer.empty()) 165 { 166 for (const dsp56k::TWord w : m_wordBuffer) 167 WavWriter::writeWord(m_byteBuffer, w); 168 169 if(m_measureSilence) 170 { 171 bool isSilence = true; 172 173 for (const dsp56k::TWord w : m_wordBuffer) 174 { 175 constexpr dsp56k::TWord silenceThreshold = 0x1ff; 176 const bool silence = w < silenceThreshold || w >= (0xffffff - silenceThreshold); 177 if(!silence) 178 { 179 isSilence = false; 180 break; 181 } 182 } 183 184 if(foundNonSilence && isSilence) 185 { 186 m_silenceDuration += static_cast<uint32_t>(m_wordBuffer.size() >> 1); 187 } 188 else if(!isSilence) 189 { 190 m_silenceDuration = 0; 191 foundNonSilence = true; 192 } 193 } 194 195 m_wordBuffer.clear(); 196 } 197 198 if(!m_byteBuffer.empty()) 199 { 200 if(writer.write(m_filename, 24, false, 2, static_cast<int>(m_samplerate), &m_byteBuffer[0], m_byteBuffer.size())) 201 { 202 m_byteBuffer.clear(); 203 } 204 else if(m_finished) 205 { 206 LOG("Unable to write data to file " << m_filename << " but termination requested, file is missing data"); 207 break; 208 } 209 } 210 211 std::this_thread::sleep_for(std::chrono::milliseconds(500)); 212 } 213 } 214 }