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 constexpr 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
openmsx::SoundDevice::setInputRate
void setInputRate(unsigned sampleRate)
Definition: SoundDevice.hh:108
openmsx::ResampledSoundDevice
Definition: ResampledSoundDevice.hh:15
openmsx::WavData
Definition: WavData.hh:13
serialize.hh
openmsx::DeviceConfig
Definition: DeviceConfig.hh:19
openmsx::DUMMY_INPUT_RATE
constexpr unsigned DUMMY_INPUT_RATE
Definition: CassettePlayer.cc:57
MSXException.hh
openmsx::MSXException
Definition: MSXException.hh:9
openmsx::DeviceConfig::getCliComm
CliComm & getCliComm() const
Definition: DeviceConfig.cc:18
openmsx::CliComm::printWarning
void printWarning(std::string_view message)
Definition: CliComm.cc:10
openmsx::SoundDevice::getInputRate
unsigned getInputRate() const
Definition: SoundDevice.hh:109
openmsx::SoundDevice::registerSound
void registerSound(const DeviceConfig &config)
Registers this sound device with the Mixer.
Definition: SoundDevice.cc:90
openmsx::SamplePlayer::repeat
void repeat(unsigned sampleNum)
Keep on repeating the given sample data.
Definition: SamplePlayer.cc:91
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
openmsx::SamplePlayer::play
void play(unsigned sampleNum)
Start playing a (new) sample.
Definition: SamplePlayer.cc:63
INSTANTIATE_SERIALIZE_METHODS
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
openmsx::SamplePlayer::reset
void reset()
Definition: SamplePlayer.cc:57
openmsx::SoundDevice::unregisterSound
void unregisterSound()
Unregisters this sound device with the Mixer.
Definition: SoundDevice.cc:131
FileContext.hh
openmsx::SamplePlayer::serialize
void serialize(Archive &ar, unsigned version)
Definition: SamplePlayer.cc:132
openmsx::SamplePlayer::isPlaying
bool isPlaying() const
Is there currently playing a sample.
Definition: SamplePlayer.hh:42
gl::context
std::unique_ptr< Context > context
Definition: GLContext.cc:9
openmsx::SamplePlayer::SamplePlayer
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
openmsx::SamplePlayer::stopRepeat
void stopRepeat()
Stop repeat mode.
Definition: SamplePlayer.hh:39
openmsx::SamplePlayer
Definition: SamplePlayer.hh:10
SamplePlayer.hh
CliComm.hh
DeviceConfig.hh
openmsx::MSXException::getMessage
const std::string & getMessage() const &
Definition: MSXException.hh:23
strCat
std::string strCat(Ts &&...ts)
Definition: strCat.hh:573
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
openmsx::SamplePlayer::~SamplePlayer
~SamplePlayer()
Definition: SamplePlayer.cc:52
openmsx::ResampledSoundDevice::createResampler
void createResampler()
Definition: ResampledSoundDevice.cc:57
openmsx::systemFileContext
FileContext systemFileContext()
Definition: FileContext.cc:149