| 1 |
#include "WavImage.hh" |
| 2 |
|
| 3 |
#include "File.hh" |
| 4 |
#include "FilePool.hh" |
| 5 |
#include "Filename.hh" |
| 6 |
|
| 7 |
#include "Math.hh" |
| 8 |
#include "narrow.hh" |
| 9 |
#include "xrange.hh" |
| 10 |
|
| 11 |
#include <algorithm> |
| 12 |
#include <array> |
| 13 |
#include <cassert> |
| 14 |
#include <map> |
| 15 |
|
| 16 |
namespace openmsx { |
| 17 |
|
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
class DCFilter { |
| 22 |
public: |
| 23 |
void setFreq(unsigned sampleFreq) { |
| 24 |
const float cutOffFreq = 800.0f; |
| 25 |
R = 1.0f - ((float(2 * Math::pi) * cutOffFreq) / narrow_cast<float>(sampleFreq)); |
| 26 |
} |
| 27 |
[[nodiscard]] int16_t operator()(int16_t x) { |
| 28 |
float t1 = R * t0 + narrow_cast<float>(x); |
| 29 |
auto y = Math::clipToInt16(narrow_cast<int>(t1 - t0)); |
| 30 |
t0 = t1; |
| 31 |
return y; |
| 32 |
} |
| 33 |
private: |
| 34 |
float R = 0.0f; |
| 35 |
float t0 = 0.0f; |
| 36 |
}; |
| 37 |
|
| 38 |
|
| 39 |
class WavImageCache |
| 40 |
{ |
| 41 |
public: |
| 42 |
struct WavInfo { |
| 43 |
WavData wav; |
| 44 |
Sha1Sum sum; |
| 45 |
}; |
| 46 |
|
| 47 |
WavImageCache(const WavImageCache&) = delete; |
| 48 |
WavImageCache(WavImageCache&&) = delete; |
| 49 |
WavImageCache& operator=(const WavImageCache&) = delete; |
| 50 |
WavImageCache& operator=(WavImageCache&&) = delete; |
| 51 |
|
| 52 |
static WavImageCache& instance(); |
| 53 |
const WavInfo& get(const std::string& filename, FilePool& filePool); |
| 54 |
void release(const WavData* wav); |
| 55 |
|
| 56 |
private: |
| 57 |
WavImageCache() = default; |
| 58 |
~WavImageCache(); |
| 59 |
|
| 60 |
|
| 61 |
struct Entry { |
| 62 |
unsigned refCount = 0; |
| 63 |
WavInfo info; |
| 64 |
}; |
| 65 |
std::map<std::string, Entry, std::less<>> cache; |
| 66 |
}; |
| 67 |
|
| 68 |
WavImageCache::~WavImageCache() |
| 69 |
{ |
| 70 |
assert(cache.empty()); |
| 71 |
} |
| 72 |
|
| 73 |
WavImageCache& WavImageCache::instance() |
| 74 |
{ |
| 75 |
static WavImageCache wavImageCache; |
| 76 |
return wavImageCache; |
| 77 |
} |
| 78 |
|
| 79 |
const WavImageCache::WavInfo& WavImageCache::get(const std::string& filename, FilePool& filePool) |
| 80 |
{ |
| 81 |
|
| 82 |
|
| 83 |
auto it = cache.find(filename); |
| 84 |
if (it == cache.end()) { |
| 85 |
File file(filename); |
| 86 |
Entry entry; |
| 87 |
entry.info.sum = filePool.getSha1Sum(file, filename); |
| 88 |
entry.info.wav = WavData(std::move(file), DCFilter{}); |
| 89 |
it = cache.try_emplace(filename, std::move(entry)).first; |
| 90 |
} |
| 91 |
auto& entry = it->second; |
| 92 |
++entry.refCount; |
| 93 |
return entry.info; |
| 94 |
|
| 95 |
} |
| 96 |
|
| 97 |
void WavImageCache::release(const WavData* wav) |
| 98 |
{ |
| 99 |
|
| 100 |
auto it = std::ranges::find(cache, wav, [](auto& pr) { return &pr.second.info.wav; }); |
| 101 |
assert(it != end(cache)); |
| 102 |
auto& entry = it->second; |
| 103 |
--entry.refCount; |
| 104 |
if (entry.refCount == 0) { |
| 105 |
cache.erase(it); |
| 106 |
} |
| 107 |
} |
| 108 |
|
| 109 |
|
| 110 |
WavImage::WavImage(const Filename& filename, FilePool& filePool) |
| 111 |
{ |
| 112 |
const auto& entry = WavImageCache::instance().get(filename.getResolved(), filePool); |
| 113 |
wav = &entry.wav; |
| 114 |
setSha1Sum(entry.sum); |
| 115 |
clock.setFreq(wav->getFreq()); |
| 116 |
|
| 117 |
setFirstFileType(FileType::UNKNOWN, filename); |
| 118 |
} |
| 119 |
|
| 120 |
WavImage::~WavImage() |
| 121 |
{ |
| 122 |
WavImageCache::instance().release(wav); |
| 123 |
} |
| 124 |
|
| 125 |
int16_t WavImage::getSampleAt(EmuTime time) const |
| 126 |
{ |
| 127 |
|
| 128 |
|
| 129 |
|
| 130 |
|
| 131 |
|
| 132 |
|
| 133 |
|
| 134 |
|
| 135 |
|
| 136 |
|
| 137 |
auto [sample, x] = clock.getTicksTillAsIntFloat(time); |
| 138 |
std::array<float, 4> p = { |
| 139 |
float(wav->getSample(sample - 1)), |
| 140 |
float(wav->getSample(sample + 0)), |
| 141 |
float(wav->getSample(sample + 1)), |
| 142 |
float(wav->getSample(sample + 2)) |
| 143 |
}; |
| 144 |
return Math::clipToInt16(int(Math::cubicHermite(p, x))); |
| 145 |
} |
| 146 |
|
| 147 |
EmuTime WavImage::getEndTime() const |
| 148 |
{ |
| 149 |
DynamicClock clk(clock); |
| 150 |
clk += wav->getSize(); |
| 151 |
return clk.getTime(); |
| 152 |
} |
| 153 |
|
| 154 |
unsigned WavImage::getFrequency() const |
| 155 |
{ |
| 156 |
return clock.getFreq(); |
| 157 |
} |
| 158 |
|
| 159 |
void WavImage::fillBuffer(unsigned pos, std::span<float*, 1> bufs, unsigned num) const |
| 160 |
{ |
| 161 |
if (pos < wav->getSize()) { |
| 162 |
for (auto i : xrange(num)) { |
| 163 |
bufs[0][i] = wav->getSample(pos + i); |
| 164 |
} |
| 165 |
} else { |
| 166 |
bufs[0] = nullptr; |
| 167 |
} |
| 168 |
} |
| 169 |
|
| 170 |
float WavImage::getAmplificationFactorImpl() const |
| 171 |
{ |
| 172 |
return 1.0f / 32768; |
| 173 |
} |
| 174 |
|
| 175 |
} |