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