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;
32 float t0 = 0.0f;
33};
34
35
37{
38public:
43
44 WavImageCache(const WavImageCache&) = delete;
46
47 static WavImageCache& instance();
48 const WavInfo& get(const Filename& filename, FilePool& filePool);
49 void release(const WavData* wav);
50
51private:
52 WavImageCache() = default;
54
55 // typically contains very few elements, but values need stable addresses
56 struct Entry {
57 unsigned refCount = 0;
58 WavInfo info;
59 };
60 std::map<std::string, Entry> cache;
61};
62
63WavImageCache::~WavImageCache()
64{
65 assert(cache.empty());
66}
67
69{
70 static WavImageCache wavImageCache;
71 return wavImageCache;
72}
73
75{
76 // Reading file or parsing as .wav may throw, so only create cache
77 // entry after all went well.
78 auto it = cache.find(filename.getResolved());
79 if (it == cache.end()) {
80 File file(filename);
81 Entry entry;
82 entry.info.sum = filePool.getSha1Sum(file);
83 entry.info.wav = WavData(std::move(file), DCFilter{});
84 it = cache.try_emplace(filename.getResolved(), std::move(entry)).first;
85 }
86 auto& entry = it->second;
87 ++entry.refCount;
88 return entry.info;
89
90}
91
93{
94 // cache contains very few entries, so linear search is ok
95 auto it = ranges::find(cache, wav, [](auto& pr) { return &pr.second.info.wav; });
96 assert(it != end(cache));
97 auto& entry = it->second;
98 --entry.refCount; // decrease reference count
99 if (entry.refCount == 0) {
100 cache.erase(it);
101 }
102}
103
104
105// Note: type detection not implemented yet for WAV images
106WavImage::WavImage(const Filename& filename, FilePool& filePool)
107 : clock(EmuTime::zero())
108{
109 const auto& entry = WavImageCache::instance().get(filename, filePool);
110 wav = &entry.wav;
111 setSha1Sum(entry.sum);
112 clock.setFreq(wav->getFreq());
113}
114
119
120int16_t WavImage::getSampleAt(EmuTime::param time) const
121{
122 // The WAV file is typically sampled at 44kHz, but the MSX may sample
123 // the signal at arbitrary moments in time. Initially we would simply
124 // returned the closest older sample point (sample-and-hold
125 // resampling). Now we perform cubic interpolation between the 4
126 // surrounding sample points. Presumably this results in more accurately
127 // timed zero-crossings of the signal.
128 //
129 // Thanks to 'p_gimeno' for figuring out that cubic resampling makes
130 // the tape "Ingrid's back" sha1:9493e8851e9f173b67670a9a3de4645918ef436f
131 // work in openMSX (with sample-and-hold it didn't work).
132 auto [sample, x] = clock.getTicksTillAsIntFloat(time);
133 std::array<float, 4> p = {
134 float(wav->getSample(unsigned(sample) - 1)), // intentional: underflow wraps to UINT_MAX
135 float(wav->getSample(sample + 0)),
136 float(wav->getSample(sample + 1)),
137 float(wav->getSample(sample + 2))
138 };
139 return Math::clipToInt16(int(Math::cubicHermite(p, x)));
140}
141
142EmuTime WavImage::getEndTime() const
143{
144 DynamicClock clk(clock);
145 clk += wav->getSize();
146 return clk.getTime();
147}
148
150{
151 return clock.getFreq();
152}
153
154void WavImage::fillBuffer(unsigned pos, std::span<float*, 1> bufs, unsigned num) const
155{
156 if (pos < wav->getSize()) {
157 for (auto i : xrange(num)) {
158 bufs[0][i] = wav->getSample(pos + i);
159 }
160 } else {
161 bufs[0] = nullptr;
162 }
163}
164
166{
167 return 1.0f / 32768;
168}
169
170} // 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:18
const std::string & getResolved() const &
Definition Filename.hh:47
This class represents the result of a sha1 calculation (a 160-bit value).
Definition sha1.hh:23
int16_t getSample(unsigned pos) const
Definition WavData.hh:38
unsigned getSize() const
Definition WavData.hh:37
unsigned getFreq() const
Definition WavData.hh:36
void release(const WavData *wav)
Definition WavImage.cc:92
static WavImageCache & instance()
Definition WavImage.cc:68
WavImageCache & operator=(const WavImageCache &)=delete
WavImageCache(const WavImageCache &)=delete
const WavInfo & get(const Filename &filename, FilePool &filePool)
Definition WavImage.cc:74
WavImage(const Filename &filename, FilePool &filePool)
Definition WavImage.cc:106
~WavImage() override
Definition WavImage.cc:115
void fillBuffer(unsigned pos, std::span< float *, 1 > bufs, unsigned num) const override
Definition WavImage.cc:154
float getAmplificationFactorImpl() const override
Definition WavImage.cc:165
int16_t getSampleAt(EmuTime::param time) const override
Definition WavImage.cc:120
EmuTime getEndTime() const override
Definition WavImage.cc:142
unsigned getFrequency() const override
Definition WavImage.cc:149
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:9
auto find(InputRange &&range, const T &value)
Definition ranges.hh:160
constexpr auto xrange(T e)
Definition xrange.hh:132
constexpr auto end(const zstring_view &x)