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