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