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