SynthLab SDK
synthlabpcmsource.h
1 #ifndef _SynthLabSMPLSource_h
2 #define _SynthLabSMPLSource_h
3 
4 #include "synthfunctions.h"
5 #include "pcmsample.h"
6 
7 // -----------------------------
8 // --- SynthLab SDK File --- //
9 // ----------------------------
17 // -----------------------------------------------------------------------------
18 namespace SynthLab
19 {
20  // --- constants
21  //const uint32_t MAX_SAMPLES = 128;
22 
42  {
43  public:
44  SynthLabPCMSource() {} // default
45 
57  SynthLabPCMSource(const char* _sampleFolderPath, const char* _sampleFolderName, double _sampleRate,
58  bool pitchlessLoops = false, bool aubioSlices = false)
59  {
60  // --- use initializer
61  init(_sampleFolderPath, _sampleFolderName, _sampleRate, pitchlessLoops, aubioSlices);
62  }
63 
74  void init(const char* _sampleFolderPath, const char* _sampleFolderName,
75  double _sampleRate, bool pitchlessLoops = false, bool aubioSlices = false)
76  {
77  // --- sample directory is also name of sample set
78  sampleFolderPath = _sampleFolderPath;
79  sampleFolderName = _sampleFolderName;
80  sampleRate = _sampleRate;
81 
82  for (uint32_t i = 0; i < NUM_MIDI_NOTES; i++)
83  {
84  sampleSet[i] = nullptr;
85  }
86 
87  selectedSample = nullptr;
88 
89  // --- parse sample FILES
90  WaveFolder waveFolder(sampleFolderPath.c_str(), sampleFolderName.c_str());
91  waveFolder.parseFolder(&sampleSet[0], pitchlessLoops, aubioSlices);
92  }
93 
98  inline virtual void deleteSamples()
99  {
100  for (uint32_t i = 0; i < NUM_MIDI_NOTES; i++)
101  {
102  PCMSample* pSample = nullptr;
103  PCMSample* pDeletedSample = nullptr;
104  for (int i = 0; i<NUM_MIDI_NOTES; i++)
105  {
106  if (sampleSet[i])
107  {
108  pSample = sampleSet[i];
109  if (pSample != pDeletedSample)
110  {
111  pDeletedSample = pSample;
112  delete pSample;
113  }
114  sampleSet[i] = nullptr;
115  }
116  }
117  }
118  }
119 
124  inline virtual uint32_t getValidSampleCount()
125  {
126  uint32_t count = 0;
127  for (int i = 0; i<NUM_MIDI_NOTES; i++)
128  {
129  if (sampleSet[i]) count++;
130  }
131  return count;
132  }
133 
138  inline virtual bool haveValidSamples()
139  {
140  if (getValidSampleCount() > 0)
141  return true;
142  return false;
143  }
144 
150 
157  inline virtual double selectSample(double oscFrequency)
158  {
159  uint32_t midiNote = midiNoteNumberFromOscFrequency(oscFrequency);
160  selectedSample = sampleSet[midiNote];
161 
162  if (!selectedSample) return 0.0;
163  double inc = 0.0;
164 
165  if (selectedSample->isPitchless())
166  {
167  inc = 1.0;
168  }
169  else
170  {
171  // --- get unity note frequency
172  double dUnityFrequency = midiNoteNumberToOscFrequency(selectedSample->getUnityMIDINote());
173 
174  // --- calculate increment
175  inc = oscFrequency / dUnityFrequency;
176  }
177 
178  return inc;
179  }
180 
194  inline virtual PCMSampleOutput readSample(double& readIndex, double inc)
195  {
196  PCMSampleOutput output;
197 
198  if (!selectedSample)
199  return output; // auto 0.0s
200 
201  if (selectedSample->getNumChannels() == 1)
202  output.numActiveChannels = 1;
203  else
204  output.numActiveChannels = 2;
205 
206  if (readIndex < 0)
207  return output; // auto 0.0s
208 
209  double numChannels = (double)selectedSample->getNumChannels();
210 
211  if (selectedSample->getLoopCount() > 0)
212  {
213  // --- use loop points for looping
214  if (loopMode == SampleLoopMode::sustain)
215  {
216  if (readIndex > (double)(selectedSample->getLoopEndIndex()) / numChannels)
217  readIndex = readIndex - (double)(selectedSample->getLoopEndIndex()) / numChannels + (double)(selectedSample->getLoopStartIndex()) / numChannels;
218  }
219  // --- use loop points for looping
220  else if (loopMode == SampleLoopMode::loop)
221  {
222  if (readIndex > (double)(selectedSample->getSampleCount() - numChannels - 1) / numChannels)
223  readIndex = 0;
224  }
225  }
226  else // there are no loops
227  {
228  if (loopMode == SampleLoopMode::sustain) // use end->start samples
229  {
230  if (readIndex > (double)(selectedSample->getSampleCount() - numChannels - 1) / numChannels)
231  {
232  readIndex = -1;
233  return output;
234  }
235  }
236  else if (loopMode == SampleLoopMode::oneShot) // use end->start samples
237  {
238  if (readIndex > (double)(selectedSample->getSampleCount() - numChannels - 1) / numChannels)
239  {
240  readIndex = -1;
241  return output;
242  }
243  }
244  else if (loopMode == SampleLoopMode::loop)
245  {
246  if (readIndex > (double)(selectedSample->getSampleCount() - numChannels - 1) / numChannels)
247  readIndex = 0;
248  }
249 
250  }
251 
252  // --- split the fractional index into int.frac parts
253  double dIntPart = 0.0;
254  double fracPart = modf(readIndex, &dIntPart);
255  uint32_t nReadIndex = (uint32_t)dIntPart;
256 
257  // --- mono or stereo file? CURRENTLY ONLY SUPPORTING THESE 2
258  if (selectedSample->getNumChannels() == 1)
259  {
260  int nReadIndexNext = nReadIndex + 1 > selectedSample->getSampleCount() - 1 ? 0 : nReadIndex + 1;
261 
262  // interpolate between the two
263  output.audioOutput[LEFT_CHANNEL] = doLinearInterpolation(0, 1, selectedSample->getSampleBuffer()[nReadIndex], selectedSample->getSampleBuffer()[nReadIndexNext], fracPart);
264  output.audioOutput[RIGHT_CHANNEL] = output.audioOutput[LEFT_CHANNEL];
265 
266  readIndex += inc;
267  }
268  else if (selectedSample->getNumChannels() == 2)
269  {
270  // --- interpolate across interleaved buffer!
271  int nReadIndexLeft = (int)readIndex * 2;
272 
273  // --- setup second index for interpolation; wrap the buffer if needed, we know last sample is Right channel
274  // so reset to top (the 0 after ?)
275  int nReadIndexNextLeft = nReadIndexLeft + 2 > selectedSample->getSampleCount() - 1 ? 0 : nReadIndexLeft + 2;
276 
277  // --- interpolate between the two
278  output.audioOutput[LEFT_CHANNEL] = doLinearInterpolation(0, 1, selectedSample->getSampleBuffer()[nReadIndexLeft], selectedSample->getSampleBuffer()[nReadIndexNextLeft], fracPart);
279 
280  // --- do the right channel
281  int nReadIndexRight = nReadIndexLeft + 1;
282 
283  // --- find the next one, skipping over, note wrap goes to index 1 ---> 1
284  int nReadIndexNextRight = nReadIndexRight + 2 > selectedSample->getSampleCount() - 1 ? 1 : nReadIndexRight + 2;
285 
286  // --- interpolate between the two
287  output.audioOutput[RIGHT_CHANNEL] = doLinearInterpolation(0, 1, selectedSample->getSampleBuffer()[nReadIndexRight], selectedSample->getSampleBuffer()[nReadIndexNextRight], fracPart);
288 
289  readIndex += inc;
290  }
291  return output;
292  }
293 
296  virtual void setSampleLoopMode(SampleLoopMode _loopMode) { loopMode = _loopMode; }
297 
298  protected:
299  // --- 128 samples
302  std::string sampleFolderPath;
303  std::string sampleFolderName;
304  double sampleRate = 0.0;
305  SampleLoopMode loopMode = SampleLoopMode::sustain;
306  };
307 
308 }
309 #endif // definer
310 
Opens a folder full of WAV files and gleans information about the files to prep them for parsing and ...
Definition: pcmsample.h:202
uint32_t numActiveChannels
number of active channels; not used in SynthLab but available
Definition: synthbase.h:736
Opens a WAV file and extracts contents into a floating point buffer, regardless of original datatypes...
Definition: pcmsample.h:109
uint32_t parseFolder(PCMSample **sampleSet, bool pitchlessLoops, bool aubioSlices=false)
The main function that opens a folder, creates the WAV information map, and then parses the files in ...
Definition: pcmsample.cpp:822
PCMSample * selectedSample
currently selected sample
Definition: synthlabpcmsource.h:301
virtual void deleteSamples()
Delete all samples from memory.
Definition: synthlabpcmsource.h:98
double doLinearInterpolation(double x1, double x2, double y1, double y2, double x)
performs linear interpolation of x distance between two (x,y) points; returns interpolated value ...
Definition: synthfunctions.h:1387
See also Designing Software Synthesizers in C++ 2nd Ed. by Will Pirkle.
Storage for a set of PCM samples that constitute a patch or instrument.
Definition: synthlabpcmsource.h:41
Interface for PCM sample sources.
Definition: synthbase.h:766
virtual void setSampleLoopMode(SampleLoopMode _loopMode)
Definition: synthlabpcmsource.h:296
virtual bool haveValidSamples()
Find all non-null sample pointers.
Definition: synthlabpcmsource.h:138
PCMSample * sampleSet[NUM_MIDI_NOTES]
one PCM sample pointer per note
Definition: synthlabpcmsource.h:300
Definition: addosccore.cpp:4
uint32_t getNumChannels()
Immutable variables can only be set during file parse.
Definition: pcmsample.h:131
std::string sampleFolderPath
folder of wave files
Definition: synthlabpcmsource.h:302
virtual PCMSampleOutput readSample(double &readIndex, double inc)
Read and interpolate the table; uses linear interpolation but could be changed to 4th order LaGrange ...
Definition: synthlabpcmsource.h:194
std::string sampleFolderName
name of patch
Definition: synthlabpcmsource.h:303
uint32_t getUnityMIDINote()
Mutable variables that may need to be re-calculated after parsing.
Definition: pcmsample.h:153
virtual uint32_t getValidSampleCount()
query for valid sample count (not used in SynthLab but avialable)
Definition: synthlabpcmsource.h:124
uint32_t getLoopEndIndex()
Mutable variables that may need to be re-calculated after parsing.
Definition: pcmsample.h:150
uint32_t getLoopStartIndex()
Mutable variables that may need to be re-calculated after parsing.
Definition: pcmsample.h:147
Holds the audio output samples from reading a PCM sample file.
Definition: synthbase.h:733
SampleLoopMode
Definition: synthbase.h:741
virtual double selectSample(double oscFrequency)
Selects a PCM sample based on the target oscillator frequency.
Definition: synthlabpcmsource.h:157
uint32_t getSampleCount()
Immutable variables can only be set during file parse.
Definition: pcmsample.h:133
SynthLabPCMSource(const char *_sampleFolderPath, const char *_sampleFolderName, double _sampleRate, bool pitchlessLoops=false, bool aubioSlices=false)
Parses a folder full of wave files and extracts the PCM samples.
Definition: synthlabpcmsource.h:57
uint32_t midiNoteNumberFromOscFrequency(double oscillatorFrequency, double frequencyA440=440.0)
use lookup table to find pitch shift multipliers, uses linear interpolation
Definition: synthfunctions.h:1485
const uint32_t NUM_MIDI_NOTES
Definition: synthconstants.h:618
double sampleRate
fs
Definition: synthlabpcmsource.h:304
~SynthLabPCMSource()
Destructor nothing to do, samples were deleted prior to destruction here.
Definition: synthlabpcmsource.h:149
SampleLoopMode loopMode
different looping modes
Definition: synthlabpcmsource.h:305
uint32_t getLoopCount()
Mutable variables that may need to be re-calculated after parsing.
Definition: pcmsample.h:144
void init(const char *_sampleFolderPath, const char *_sampleFolderName, double _sampleRate, bool pitchlessLoops=false, bool aubioSlices=false)
Separated initializer, parses a folder full of wave files and extracts the PCM samples.
Definition: synthlabpcmsource.h:74
hard-coded arrays of FIR filter coefficients for the sample rate conversion objects (Interpolator and...
double audioOutput[STEREO_CHANNELS]
array of audio output samples
Definition: synthbase.h:735
double midiNoteNumberToOscFrequency(uint32_t midiNoteNumber, double frequencyA440=440.0)
converts a MIDI note number into a corresponding oscillator frequency in Hz
Definition: synthfunctions.h:1513