openMSX
WavImage.cc
Go to the documentation of this file.
1#include "WavImage.hh"
2#include "File.hh"
3#include "Filename.hh"
4#include "FilePool.hh"
5#include "Math.hh"
6#include "narrow.hh"
7#include "ranges.hh"
8#include "xrange.hh"
9#include <cassert>
10#include <array>
11#include <map>
12
13namespace openmsx {
14
15// DC-removal filter
16// y(n) = x(n) - x(n-1) + R * y(n-1)
17// see comments in MSXMixer.cc for more details
18class DCFilter {
19public:
20 void setFreq(unsigned sampleFreq) {
21 const float cutOffFreq = 800.0f; // trial-and-error
22 R = 1.0f - ((float(2 * Math::pi) * cutOffFreq) / narrow_cast<float>(sampleFreq));
23 }
24 [[nodiscard]] int16_t operator()(int16_t x) {
25 float t1 = R * t0 + narrow_cast<float>(x);
26 auto y = Math::clipToInt16(narrow_cast<int>(t1 - t0));
27 t0 = t1;
28 return y;
29 }
30private:
31 float R = 0.0f;
32 float t0 = 0.0f;
33};
34
35
37{
38public:
43
44 WavImageCache(const WavImageCache&) = delete;
48
49 static WavImageCache& instance();
50 const WavInfo& get(const Filename& filename, FilePool& filePool);
51 void release(const WavData* wav);
52
53private:
54 WavImageCache() = default;
56
57 // typically contains very few elements, but values need stable addresses
58 struct Entry {
59 unsigned refCount = 0;
60 WavInfo info;
61 };
62 std::map<std::string, Entry, std::less<>> cache;
63};
64
65WavImageCache::~WavImageCache()
66{
67 assert(cache.empty());
68}
69
71{
72 static WavImageCache wavImageCache;
73 return wavImageCache;
74}
75
77{
78 // Reading file or parsing as .wav may throw, so only create cache
79 // entry after all went well.
80 auto it = cache.find(filename.getResolved());
81 if (it == cache.end()) {
82 File file(filename);
83 Entry entry;
84 entry.info.sum = filePool.getSha1Sum(file);
85 entry.info.wav = WavData(std::move(file), DCFilter{});
86 it = cache.try_emplace(filename.getResolved(), std::move(entry)).first;
87 }
88 auto& entry = it->second;
89 ++entry.refCount;
90 return entry.info;
91
92}
93
95{
96 // cache contains very few entries, so linear search is ok
97 auto it = ranges::find(cache, wav, [](auto& pr) { return &pr.second.info.wav; });
98 assert(it != end(cache));
99 auto& entry = it->second;
100 --entry.refCount; // decrease reference count
101 if (entry.refCount == 0) {
102 cache.erase(it);
103 }
104}
105
106
107// Note: type detection not implemented yet for WAV images
108WavImage::WavImage(const Filename& filename, FilePool& filePool)
109{
110 const auto& entry = WavImageCache::instance().get(filename, filePool);
111 wav = &entry.wav;
112 setSha1Sum(entry.sum);
113 clock.setFreq(wav->getFreq());
114}
115
120
121int16_t WavImage::getSampleAt(EmuTime::param time) const
122{
123 // The WAV file is typically sampled at 44kHz, but the MSX may sample
124 // the signal at arbitrary moments in time. Initially we would simply
125 // returned the closest older sample point (sample-and-hold
126 // resampling). Now we perform cubic interpolation between the 4
127 // surrounding sample points. Presumably this results in more accurately
128 // timed zero-crossings of the signal.
129 //
130 // Thanks to 'p_gimeno' for figuring out that cubic resampling makes
131 // the tape "Ingrid's back" sha1:9493e8851e9f173b67670a9a3de4645918ef436f
132 // work in openMSX (with sample-and-hold it didn't work).
133 auto [sample, x] = clock.getTicksTillAsIntFloat(time);
134 std::array<float, 4> p = {
135 float(wav->getSample(unsigned(sample) - 1)), // intentional: underflow wraps to UINT_MAX
136 float(wav->getSample(sample + 0)),
137 float(wav->getSample(sample + 1)),
138 float(wav->getSample(sample + 2))
139 };
140 return Math::clipToInt16(int(Math::cubicHermite(p, x)));
141}
142
143EmuTime WavImage::getEndTime() const
144{
145 DynamicClock clk(clock);
146 clk += wav->getSize();
147 return clk.getTime();
148}
149
151{
152 return clock.getFreq();
153}
154
155void WavImage::fillBuffer(unsigned pos, std::span<float*, 1> bufs, unsigned num) const
156{
157 if (pos < wav->getSize()) {
158 for (auto i : xrange(num)) {
159 bufs[0][i] = wav->getSample(pos + i);
160 }
161 } else {
162 bufs[0] = nullptr;
163 }
164}
165
167{
168 return 1.0f / 32768;
169}
170
171} // namespace openmsx
void setSha1Sum(const Sha1Sum &sha1sum)
void setFreq(unsigned sampleFreq)
Definition WavImage.cc:20
int16_t operator()(int16_t x)
Definition WavImage.cc:24
Represents a clock with a variable frequency.
IntegralFractional getTicksTillAsIntFloat(EmuTime::param e) const
unsigned getFreq() const
Returns the frequency (in Hz) at which this clock ticks.
void setFreq(unsigned freq)
Change the frequency at which this clock ticks.
EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
Sha1Sum getSha1Sum(File &file)
Calculate sha1sum for the given File object.
Definition FilePool.cc:58
This class represents a filename.
Definition Filename.hh:20
const std::string & getResolved() const &
Definition Filename.hh:38
This class represents the result of a sha1 calculation (a 160-bit value).
Definition sha1.hh:24
size_t getSize() const
Definition WavData.hh:40
int16_t getSample(size_t pos) const
Definition WavData.hh:41
unsigned getFreq() const
Definition WavData.hh:39
void release(const WavData *wav)
Definition WavImage.cc:94
WavImageCache(WavImageCache &&)=delete
static WavImageCache & instance()
Definition WavImage.cc:70
WavImageCache & operator=(const WavImageCache &)=delete
WavImageCache & operator=(WavImageCache &&)=delete
WavImageCache(const WavImageCache &)=delete
const WavInfo & get(const Filename &filename, FilePool &filePool)
Definition WavImage.cc:76
WavImage(const Filename &filename, FilePool &filePool)
Definition WavImage.cc:108
~WavImage() override
Definition WavImage.cc:116
void fillBuffer(unsigned pos, std::span< float *, 1 > bufs, unsigned num) const override
Definition WavImage.cc:155
float getAmplificationFactorImpl() const override
Definition WavImage.cc:166
int16_t getSampleAt(EmuTime::param time) const override
Definition WavImage.cc:121
EmuTime getEndTime() const override
Definition WavImage.cc:143
unsigned getFrequency() const override
Definition WavImage.cc:150
constexpr double pi
Definition Math.hh:24
constexpr float cubicHermite(std::span< const float, 4 > y, float x)
Definition Math.hh:167
int16_t clipToInt16(T x)
Clip x to range [-32768,32767].
Definition Math.hh:47
This file implemented 3 utility functions:
Definition Autofire.cc:11
auto find(InputRange &&range, const T &value)
Definition ranges.hh:162
constexpr auto xrange(T e)
Definition xrange.hh:132
constexpr auto end(const zstring_view &x)