openMSX
SamplePlayer.cc
Go to the documentation of this file.
1 #include "SamplePlayer.hh"
2 #include "DeviceConfig.hh"
3 #include "CliComm.hh"
4 #include "FileContext.hh"
5 #include "MSXException.hh"
6 #include "serialize.hh"
7 #include "xrange.hh"
8 #include <cassert>
9 #include <memory>
10 
11 namespace openmsx {
12 
13 constexpr unsigned DUMMY_INPUT_RATE = 44100; // actual rate depends on .wav files
14 
15 [[nodiscard]] static auto loadSamples(
16  std::string_view name, const DeviceConfig& config,
17  std::string_view baseName, std::string_view alternativeName,
18  unsigned numSamples)
19 {
20  dynarray<WavData> result(numSamples); // initialize with empty WAVs
21 
22  bool alreadyWarned = false;
23  auto context = systemFileContext();
24  for (auto i : xrange(numSamples)) {
25  try {
26  auto filename = tmpStrCat(baseName, i, ".wav");
27  result[i] = WavData(context.resolve(filename));
28  } catch (MSXException& e1) {
29  try {
30  if (alternativeName.empty()) throw;
31  auto filename = tmpStrCat(
32  alternativeName, i, ".wav");
33  result[i] = WavData(context.resolve(filename));
34  } catch (MSXException& /*e2*/) {
35  if (!alreadyWarned) {
36  alreadyWarned = true;
37  // print message from the 1st error
38  config.getCliComm().printWarning(
39  "Couldn't read ", name, " sample data: ",
40  e1.getMessage(),
41  ". Continuing without sample data.");
42  }
43  }
44  }
45  }
46  return result;
47 }
48 
49 SamplePlayer::SamplePlayer(const std::string& name_, static_string_view desc,
50  const DeviceConfig& config,
51  std::string_view samplesBaseName, unsigned numSamples,
52  std::string_view alternativeName)
53  : ResampledSoundDevice(config.getMotherBoard(), name_, desc, 1, DUMMY_INPUT_RATE, false)
54  , samples(loadSamples(name_, config, samplesBaseName, alternativeName, numSamples))
55 {
56  registerSound(config);
57  reset();
58 
59  // avoid UMR on serialize
60  index = 0;
61 }
62 
64 {
66 }
67 
69 {
70  currentSampleNum = unsigned(-1);
71  stopRepeat();
72 }
73 
74 void SamplePlayer::play(unsigned sampleNum)
75 {
76  assert(sampleNum < samples.size());
77  currentSampleNum = sampleNum;
78  index = 0;
79  setWavParams();
80 }
81 
82 void SamplePlayer::setWavParams()
83 {
84  if ((currentSampleNum < samples.size()) &&
85  samples[currentSampleNum].getSize()) {
86  auto& wav = samples[currentSampleNum];
87  bufferSize = wav.getSize();
88 
89  unsigned freq = wav.getFreq();
90  if (freq != getInputRate()) {
91  // this potentially switches resampler, so there might be
92  // some dropped samples if this is done in the middle of
93  // playing, though this shouldn't happen often (or at all)
94  setInputRate(freq);
96  }
97  } else {
98  reset();
99  }
100 }
101 
102 void SamplePlayer::repeat(unsigned sampleNum)
103 {
104  assert(sampleNum < samples.size());
105  nextSampleNum = sampleNum;
106  if (!isPlaying()) {
107  doRepeat();
108  }
109 }
110 
111 void SamplePlayer::generateChannels(float** bufs, unsigned num)
112 {
113  // Single channel device: replace content of bufs[0] (not add to it).
114  if (!isPlaying()) {
115  bufs[0] = nullptr;
116  return;
117  }
118 
119  auto& wav = samples[currentSampleNum];
120  for (auto i : xrange(num)) {
121  if (index >= bufferSize) {
122  if (nextSampleNum != unsigned(-1)) {
123  doRepeat();
124  } else {
125  currentSampleNum = unsigned(-1);
126  // fill remaining buffer with zeros
127  do {
128  bufs[0][i++] = 0.0f;
129  } while (i < num);
130  break;
131  }
132  }
133  bufs[0][i] = 3 * wav.getSample(index++);
134  }
135 }
136 
137 void SamplePlayer::doRepeat()
138 {
139  play(nextSampleNum);
140 }
141 
142 template<typename Archive>
143 void SamplePlayer::serialize(Archive& ar, unsigned /*version*/)
144 {
145  ar.serialize("index", index,
146  "currentSampleNum", currentSampleNum,
147  "nextSampleNum", nextSampleNum);
148  if constexpr (Archive::IS_LOADER) {
149  setWavParams();
150  }
151 }
153 
154 } // namespace openmsx
bool isPlaying() const
Is there currently playing a sample.
Definition: SamplePlayer.hh:42
void serialize(Archive &ar, unsigned version)
SamplePlayer(const std::string &name, static_string_view desc, const DeviceConfig &config, std::string_view samplesBaseName, unsigned numSamples, std::string_view alternativeName={})
Definition: SamplePlayer.cc:49
void play(unsigned sampleNum)
Start playing a (new) sample.
Definition: SamplePlayer.cc:74
void repeat(unsigned sampleNum)
Keep on repeating the given sample data.
void stopRepeat()
Stop repeat mode.
Definition: SamplePlayer.hh:39
unsigned getInputRate() const
Definition: SoundDevice.hh:119
void setInputRate(unsigned sampleRate)
Definition: SoundDevice.hh:118
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:134
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:89
static_string_view
std::optional< Context > context
Definition: GLContext.cc:9
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr unsigned DUMMY_INPUT_RATE
const FileContext & systemFileContext()
Definition: FileContext.cc:157
constexpr const char *const filename
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:659
constexpr auto xrange(T e)
Definition: xrange.hh:155