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