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#include <memory>
10
11namespace openmsx {
12
13constexpr unsigned DUMMY_INPUT_RATE = 44100; // actual rate depends on .wav files
14
15[[nodiscard]] static auto loadSamples(
16 std::string_view name, const DeviceConfig& config,
17 std::string_view baseName, std::string_view alternativeName,
18 unsigned numSamples)
19{
20 dynarray<WavData> result(numSamples); // initialize with empty WAVs
21
22 bool alreadyWarned = false;
23 const auto& context = systemFileContext();
24 for (auto i : xrange(numSamples)) {
25 try {
26 auto filename = tmpStrCat(baseName, i, ".wav");
27 result[i] = WavData(context.resolve(filename));
28 } catch (MSXException& e1) {
29 try {
30 if (alternativeName.empty()) throw;
31 auto filename = tmpStrCat(
32 alternativeName, i, ".wav");
33 result[i] = WavData(context.resolve(filename));
34 } catch (MSXException& /*e2*/) {
35 if (!alreadyWarned) {
36 alreadyWarned = true;
37 // print message from the 1st error
38 config.getCliComm().printWarning(
39 "Couldn't read ", name, " sample data: ",
40 e1.getMessage(),
41 ". Continuing without sample data.");
42 }
43 }
44 }
45 }
46 return result;
47}
48
49SamplePlayer::SamplePlayer(const std::string& name_, static_string_view desc,
50 const DeviceConfig& config,
51 std::string_view samplesBaseName, unsigned numSamples,
52 std::string_view alternativeName)
53 : ResampledSoundDevice(config.getMotherBoard(), name_, desc, 1, DUMMY_INPUT_RATE, false)
54 , samples(loadSamples(name_, config, samplesBaseName, alternativeName, numSamples))
55 , index(0) // avoid UMR on serialize
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(float** bufs, unsigned num)
110{
111 // Single channel device: replace content of bufs[0] (not add to it).
112 if (!isPlaying()) {
113 bufs[0] = nullptr;
114 return;
115 }
116
117 const auto& wav = samples[currentSampleNum];
118 for (auto i : xrange(num)) {
119 if (index >= bufferSize) {
120 if (nextSampleNum != unsigned(-1)) {
121 doRepeat();
122 } else {
123 currentSampleNum = unsigned(-1);
124 // fill remaining buffer with zeros
125 do {
126 bufs[0][i++] = 0.0f;
127 } while (i < num);
128 break;
129 }
130 }
131 bufs[0][i] = 3 * wav.getSample(index++);
132 }
133}
134
135void SamplePlayer::doRepeat()
136{
137 play(nextSampleNum);
138}
139
140template<typename Archive>
141void SamplePlayer::serialize(Archive& ar, unsigned /*version*/)
142{
143 ar.serialize("index", index,
144 "currentSampleNum", currentSampleNum,
145 "nextSampleNum", nextSampleNum);
146 if constexpr (Archive::IS_LOADER) {
147 setWavParams();
148 }
149}
151
152} // 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:49
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:119
void setInputRate(unsigned sampleRate)
Definition: SoundDevice.hh:118
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:9
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr unsigned DUMMY_INPUT_RATE
const FileContext & systemFileContext()
Definition: FileContext.cc:155
constexpr const char *const filename
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:617
constexpr auto xrange(T e)
Definition: xrange.hh:133