gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

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 }