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 "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
62{
64}
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.
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:50
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:120
void setInputRate(unsigned sampleRate)
Definition: SoundDevice.hh:119
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:10
This file implemented 3 utility functions:
Definition: Autofire.cc:9
const FileContext & systemFileContext()
Definition: FileContext.cc:155
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1021
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:610
constexpr auto xrange(T e)
Definition: xrange.hh:132