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  const 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  , index(0) // avoid UMR on serialize
56 {
57  registerSound(config);
58  reset();
59 }
60 
62 {
64 }
65 
67 {
68  currentSampleNum = unsigned(-1);
69  stopRepeat();
70 }
71 
72 void SamplePlayer::play(unsigned sampleNum)
73 {
74  assert(sampleNum < samples.size());
75  currentSampleNum = sampleNum;
76  index = 0;
77  setWavParams();
78 }
79 
80 void SamplePlayer::setWavParams()
81 {
82  if ((currentSampleNum < samples.size()) &&
83  samples[currentSampleNum].getSize()) {
84  const auto& wav = samples[currentSampleNum];
85  bufferSize = wav.getSize();
86 
87  unsigned freq = wav.getFreq();
88  if (freq != getInputRate()) {
89  // this potentially switches resampler, so there might be
90  // some dropped samples if this is done in the middle of
91  // playing, though this shouldn't happen often (or at all)
92  setInputRate(freq);
94  }
95  } else {
96  reset();
97  }
98 }
99 
100 void SamplePlayer::repeat(unsigned sampleNum)
101 {
102  assert(sampleNum < samples.size());
103  nextSampleNum = sampleNum;
104  if (!isPlaying()) {
105  doRepeat();
106  }
107 }
108 
109 void SamplePlayer::generateChannels(float** bufs, unsigned num)
110 {
111  // Single channel device: replace content of bufs[0] (not add to it).
112  if (!isPlaying()) {
113  bufs[0] = nullptr;
114  return;
115  }
116 
117  const auto& wav = samples[currentSampleNum];
118  for (auto i : xrange(num)) {
119  if (index >= bufferSize) {
120  if (nextSampleNum != unsigned(-1)) {
121  doRepeat();
122  } else {
123  currentSampleNum = unsigned(-1);
124  // fill remaining buffer with zeros
125  do {
126  bufs[0][i++] = 0.0f;
127  } while (i < num);
128  break;
129  }
130  }
131  bufs[0][i] = 3 * wav.getSample(index++);
132  }
133 }
134 
135 void SamplePlayer::doRepeat()
136 {
137  play(nextSampleNum);
138 }
139 
140 template<typename Archive>
141 void SamplePlayer::serialize(Archive& ar, unsigned /*version*/)
142 {
143  ar.serialize("index", index,
144  "currentSampleNum", currentSampleNum,
145  "nextSampleNum", nextSampleNum);
146  if constexpr (Archive::IS_LOADER) {
147  setWavParams();
148  }
149 }
151 
152 } // 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:72
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:133
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:88
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:155
constexpr const char *const filename
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:617
constexpr auto xrange(T e)
Definition: xrange.hh:133