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 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 
49 private:
50  WavImageCache() = default;
51  ~WavImageCache();
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 
61 WavImageCache::~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 
118 int16_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 
140 EmuTime WavImage::getEndTime() const
141 {
142  DynamicClock clk(clock);
143  clk += wav->getSize();
144  return clk.getTime();
145 }
146 
147 unsigned WavImage::getFrequency() const
148 {
149  return clock.getFreq();
150 }
151 
152 void 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
#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: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:36
unsigned getSize() const
Definition: WavData.hh:35
unsigned getFreq() const
Definition: WavData.hh:34
void release(const WavData *wav)
Definition: WavImage.cc:90
WavImageCache & operator=(const WavImageCache &)=delete
static WavImageCache & instance()
Definition: WavImage.cc:66
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
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:266
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr const char *const filename
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:118
auto find(InputRange &&range, const T &value)
Definition: ranges.hh:129
constexpr auto xrange(T e)
Definition: xrange.hh:155
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:84