LIB: support enabled attribute for inst encoder config
[spectmorph.git] / lib / sminstrument.cc
1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2
3 #include "sminstrument.hh"
4 #include "smpugixml.hh"
5 #include "smzip.hh"
6
7 #include <map>
8 #include <memory>
9
10 using namespace SpectMorph;
11
12 using pugi::xml_document;
13 using pugi::xml_node;
14 using pugi::xml_writer;
15
16 using std::string;
17 using std::vector;
18 using std::map;
19
20 /* ------------- Sample::Shared -------------*/
21
22 // this class should never modify any data after construction
23 //  -> we can share it between different threads
24
25 Sample::Shared::Shared (const WavData& wav_data) :
26   m_wav_data (wav_data)
27 {
28   m_wav_data_hash = sha1_hash ((const guchar *) &wav_data.samples()[0], sizeof (float) * wav_data.samples().size());
29 }
30
31 string
32 Sample::Shared::wav_data_hash() const
33 {
34   return m_wav_data_hash;
35 }
36
37 const WavData&
38 SpectMorph::Sample::Shared::wav_data() const
39 {
40   return m_wav_data;
41 }
42
43 /* ------------- Sample -------------*/
44 Sample::Sample (Instrument *inst, const WavData& wav_data) :
45   instrument (inst),
46   m_shared (new Sample::Shared (wav_data))
47 {
48 }
49
50 void
51 Sample::set_marker (MarkerType marker_type, double value)
52 {
53   marker_map[marker_type] = value;
54   instrument->marker_changed();
55 }
56
57 double
58 Sample::get_marker (MarkerType marker_type) const
59 {
60   auto it = marker_map.find (marker_type);
61   if (it != marker_map.end())
62     return it->second;
63   return -1;
64 }
65
66 int
67 Sample::midi_note() const
68 {
69   return m_midi_note;
70 }
71
72 void
73 Sample::set_midi_note (int note)
74 {
75   m_midi_note = note;
76
77   instrument->update_order();
78 }
79
80 Sample::Loop
81 Sample::loop () const
82 {
83   return m_loop;
84 }
85
86 void
87 Sample::set_loop (Loop loop)
88 {
89   m_loop = loop;
90   instrument->marker_changed();
91 }
92
93 const WavData&
94 Sample::wav_data() const
95 {
96   return m_shared->wav_data();
97 }
98
99 string
100 Sample::wav_data_hash() const
101 {
102   return m_shared->wav_data_hash();
103 }
104
105 Sample::SharedP
106 Sample::shared() const
107 {
108   return m_shared;
109 }
110
111 /* ------------- Instrument -------------*/
112
113 Instrument::Instrument()
114 {
115 }
116
117 Sample *
118 Instrument::add_sample (const string& filename)
119 {
120   /* try loading file */
121   WavData wav_data;
122   if (!wav_data.load_mono (filename))
123     return nullptr;
124
125   /* new sample will be selected */
126   m_selected = samples.size();
127
128   Sample *sample = new Sample (this, wav_data);
129   samples.emplace_back (sample);
130   sample->filename  = filename;
131   sample->short_name = gen_short_name (filename);
132
133   sample->set_marker (MARKER_CLIP_START, 0.0 * 1000.0 * wav_data.samples().size() / wav_data.mix_freq());
134   sample->set_marker (MARKER_CLIP_END, 1.0 * 1000.0 * wav_data.samples().size() / wav_data.mix_freq());
135   sample->set_marker (MARKER_LOOP_START, 0.4 * 1000.0 * wav_data.samples().size() / wav_data.mix_freq());
136   sample->set_marker (MARKER_LOOP_END, 0.6 * 1000.0 * wav_data.samples().size() / wav_data.mix_freq());
137
138   signal_samples_changed();
139
140   return sample;
141 }
142
143 size_t
144 Instrument::size() const
145 {
146   return samples.size();
147 }
148
149 string
150 Instrument::name() const
151 {
152   return m_name;
153 }
154
155 Sample *
156 Instrument::sample (size_t n) const
157 {
158   if (n < samples.size())
159     return samples[n].get();
160   else
161     return nullptr;
162 }
163
164 int
165 Instrument::selected() const
166 {
167   return m_selected;
168 }
169
170 void
171 Instrument::set_selected (int sel)
172 {
173   m_selected = sel;
174
175   signal_samples_changed();
176 }
177
178 void
179 Instrument::load (const string& filename)
180 {
181   samples.clear();
182
183   char *basename = g_path_get_basename (filename.c_str());
184   m_name = basename;
185   g_free (basename);
186
187   xml_document doc;
188   doc.load_file (filename.c_str());
189   xml_node inst_node = doc.child ("instrument");
190   for (xml_node sample_node : inst_node.children ("sample"))
191     {
192       string filename = sample_node.attribute ("filename").value();
193       int midi_note = atoi (sample_node.attribute ("midi_note").value());
194       if (midi_note == 0) /* default */
195         midi_note = 69;
196
197       /* try loading file */
198       WavData wav_data;
199       if (wav_data.load_mono (filename))
200         {
201           Sample *sample = new Sample (this, wav_data);
202           samples.emplace_back (sample);
203           sample->filename  = filename;
204           sample->short_name = gen_short_name (filename);
205           sample->set_midi_note (midi_note);
206
207           xml_node clip_node = sample_node.child ("clip");
208           if (clip_node)
209             {
210               sample->set_marker (MARKER_CLIP_START, sm_atof (clip_node.attribute ("start").value()));
211               sample->set_marker (MARKER_CLIP_END, sm_atof (clip_node.attribute ("end").value()));
212             }
213           xml_node loop_node = sample_node.child ("loop");
214           if (loop_node)
215             {
216               string loop_type = loop_node.attribute ("type").value();
217               if (loop_type == "forward")
218                 sample->set_loop (Sample::Loop::FORWARD);
219               if (loop_type == "ping-pong")
220                 sample->set_loop (Sample::Loop::PING_PONG);
221               if (loop_type == "single-frame")
222                 sample->set_loop (Sample::Loop::SINGLE_FRAME);
223
224               sample->set_marker (MARKER_LOOP_START, sm_atof (loop_node.attribute ("start").value()));
225               sample->set_marker (MARKER_LOOP_END, sm_atof (loop_node.attribute ("end").value()));
226             }
227         }
228     }
229   // auto tune
230   m_auto_tune.enabled = false; // default
231   xml_node auto_tune_node = inst_node.child ("auto_tune");
232   if (auto_tune_node)
233     {
234       string method = auto_tune_node.attribute ("method").value();
235
236       if (method == "simple")
237         {
238           m_auto_tune.method = AutoTune::SIMPLE;
239           m_auto_tune.enabled = true;
240         }
241       else if (method == "all_frames")
242         {
243           m_auto_tune.method   = AutoTune::ALL_FRAMES;
244           m_auto_tune.partials = atoi (auto_tune_node.attribute ("partials").value());
245           m_auto_tune.enabled  = true;
246         }
247       else if (method == "smooth")
248         {
249           m_auto_tune.method   = AutoTune::SMOOTH;
250           m_auto_tune.partials = atoi (auto_tune_node.attribute ("partials").value());
251           m_auto_tune.time     = sm_atof (auto_tune_node.attribute ("time").value());
252           m_auto_tune.amount   = sm_atof (auto_tune_node.attribute ("amount").value());
253           m_auto_tune.enabled  = true;
254         }
255     }
256
257   // auto volume
258   m_auto_volume.enabled = false;
259   xml_node auto_volume_node = inst_node.child ("auto_volume");
260   if (auto_volume_node)
261     {
262       string method = auto_volume_node.attribute ("method").value();
263
264       if (method == "from_loop")
265         {
266           m_auto_volume.method  = AutoVolume::FROM_LOOP;
267           m_auto_volume.enabled = true;
268         }
269       else if (method == "global")
270         {
271           m_auto_volume.method  = AutoVolume::GLOBAL;
272           m_auto_volume.gain    = sm_atof (auto_volume_node.attribute ("gain").value());
273           m_auto_volume.enabled = true;
274         }
275       else
276         {
277           fprintf (stderr, "unknown auto volume method: %s\n", method.c_str());
278         }
279     }
280   m_encoder_config = EncoderConfig(); // reset
281   for (xml_node enc_node : inst_node.children ("encoder_config"))
282     {
283       EncoderEntry entry;
284       entry.param = enc_node.attribute ("param").value();
285
286       if (entry.param != "")
287         {
288           entry.value = enc_node.attribute ("value").value();
289           m_encoder_config.entries.push_back (entry);
290           m_encoder_config.enabled = true;
291         }
292     }
293
294   /* select first sample if possible */
295   if (samples.empty())
296     m_selected = -1;
297   else
298     m_selected = 0;
299   signal_samples_changed();
300 }
301
302 static bool
303 ends_with (const std::string& str, const std::string& suffix)
304 {
305   /* if suffix is .wav, match foo.wav, foo.WAV, foo.Wav, ... */
306   return str.size() >= suffix.size() &&
307          std::equal (str.end() - suffix.size(), str.end(), suffix.begin(),
308                      [] (char c1, char c2) -> bool { return tolower (c1) == tolower (c2);});
309 }
310
311 static bool
312 no_case_equal (const string& s1, const string& s2)
313 {
314   if (s1.size() != s2.size())
315     return false;
316
317   return std::equal (s1.begin(), s1.end(), s2.begin(),
318                      [] (char c1, char c2) -> bool { return tolower (c1) == tolower (c2);});
319 }
320
321 string
322 Instrument::gen_short_name (const string& filename)
323 {
324   char *gbasename = g_path_get_basename (filename.c_str());
325   string basename = gbasename;
326   g_free (gbasename);
327
328   for (auto ext : vector<string> { ".wav", ".flac", ".ogg", ".aiff" })
329     if (ends_with (basename, ext))
330       {
331         basename.resize (basename.size() - ext.size());
332       }
333   for (int i = 1; ; i++)
334     {
335       string short_name = basename;
336       if (i > 1)
337         short_name += string_printf ("-%d", i);
338
339       bool   used = false;
340
341       for (auto& sample : samples)
342         {
343           /* some filesystems are case insensitive, so we avoid short names only differ in case */
344           if (no_case_equal (sample->short_name, short_name))
345             used = true;
346         }
347
348       if (!used)
349         {
350           return short_name;
351         }
352     }
353 }
354
355 void
356 Instrument::save (const string& filename, bool zip)
357 {
358   xml_document doc;
359   xml_node inst_node = doc.append_child ("instrument");
360   for (auto& sample : samples)
361     {
362       xml_node sample_node = inst_node.append_child ("sample");
363       if (zip)
364         sample_node.append_attribute ("filename").set_value ((sample->short_name + ".wav").c_str());
365       else
366         sample_node.append_attribute ("filename").set_value (sample->filename.c_str());
367       sample_node.append_attribute ("midi_note").set_value (sample->midi_note());
368
369       xml_node clip_node = sample_node.append_child ("clip");
370       clip_node.append_attribute ("start") = string_printf ("%.3f", sample->get_marker (MARKER_CLIP_START)).c_str();
371       clip_node.append_attribute ("end") = string_printf ("%.3f", sample->get_marker (MARKER_CLIP_END)).c_str();
372
373       if (sample->loop() != Sample::Loop::NONE)
374         {
375           string loop_type;
376
377           if (sample->loop() == Sample::Loop::FORWARD)
378             loop_type = "forward";
379           if (sample->loop() == Sample::Loop::PING_PONG)
380             loop_type = "ping-pong";
381           if (sample->loop() == Sample::Loop::SINGLE_FRAME)
382             loop_type = "single-frame";
383
384           xml_node loop_node = sample_node.append_child ("loop");
385           loop_node.append_attribute ("type") = loop_type.c_str();
386           loop_node.append_attribute ("start") = string_printf ("%.3f", sample->get_marker (MARKER_LOOP_START)).c_str();
387           loop_node.append_attribute ("end") = string_printf ("%.3f", sample->get_marker (MARKER_LOOP_END)).c_str();
388         }
389     }
390   if (m_auto_tune.enabled)
391     {
392       xml_node auto_tune_node = inst_node.append_child ("auto_tune");
393       if (m_auto_tune.method == AutoTune::SIMPLE)
394         {
395           auto_tune_node.append_attribute ("method").set_value ("simple");
396         }
397       else if (m_auto_tune.method == AutoTune::ALL_FRAMES)
398         {
399           auto_tune_node.append_attribute ("method").set_value ("all_frames");
400           auto_tune_node.append_attribute ("partials") = m_auto_tune.partials;
401         }
402       else if (m_auto_tune.method == AutoTune::SMOOTH)
403         {
404           auto_tune_node.append_attribute ("method").set_value ("smooth");
405           auto_tune_node.append_attribute ("partials") = m_auto_tune.partials;
406           auto_tune_node.append_attribute ("time")     = string_printf ("%.1f", m_auto_tune.time).c_str();
407           auto_tune_node.append_attribute ("amount")   = string_printf ("%.1f", m_auto_tune.amount).c_str();
408         }
409     }
410   if (m_auto_volume.enabled)
411     {
412       xml_node auto_volume_node = inst_node.append_child ("auto_volume");
413       if (m_auto_volume.method == AutoVolume::FROM_LOOP)
414         {
415           auto_volume_node.append_attribute ("method").set_value ("from_loop");
416         }
417       if (m_auto_volume.method == AutoVolume::GLOBAL)
418         {
419           auto_volume_node.append_attribute ("method").set_value ("global");
420           auto_volume_node.append_attribute ("gain") = string_printf ("%.1f", m_auto_volume.gain).c_str();
421         }
422     }
423   if (m_encoder_config.enabled)
424     {
425       for (auto entry : m_encoder_config.entries)
426         {
427           xml_node conf_node = inst_node.append_child ("encoder_config");
428           conf_node.append_attribute ("param").set_value (entry.param.c_str());
429           conf_node.append_attribute ("value").set_value (entry.value.c_str());
430         }
431     }
432   if (zip)
433     {
434       class VectorOut : public xml_writer
435       {
436       public:
437         vector<unsigned char> vec;
438         void
439         write (const void* data, size_t size) override
440         {
441           const unsigned char *d = (const unsigned char *) data;
442
443           vec.insert (vec.end(), d, d + size);
444         }
445       } out;
446       doc.save (out);
447
448       ZipWriter writer (filename);
449       writer.add ("instrument.xml", out.vec);
450       for (size_t i = 0; i < samples.size(); i++)
451         {
452           /* we make a deep copy here, because save() is non-const */
453           WavData wav_data (samples[i]->wav_data().samples(),
454                             samples[i]->wav_data().n_channels(),
455                             samples[i]->wav_data().mix_freq(),
456                             samples[i]->wav_data().bit_depth());
457
458           vector<unsigned char> wav_file_vec;
459           wav_data.save (wav_file_vec);
460           writer.add (samples[i]->short_name + ".wav", wav_file_vec);
461         }
462     }
463   else
464     {
465       doc.save_file (filename.c_str());
466     }
467 }
468
469 void
470 Instrument::update_order()
471 {
472   Sample *ssample = sample (selected());
473   using SUPtr = std::unique_ptr<Sample>;
474   sort (samples.begin(), samples.end(),
475     [](const SUPtr& s1, const SUPtr& s2) {
476       if (s1->midi_note() > s2->midi_note())
477         return true;
478       if (s1->midi_note() < s2->midi_note())
479         return false;
480       return s1->filename < s2->filename;
481     });
482
483   for (size_t n = 0; n < samples.size(); n++)
484     if (samples[n].get() == ssample)
485       m_selected = n;
486
487   signal_samples_changed();
488 }
489
490 void
491 Instrument::marker_changed()
492 {
493   signal_marker_changed();
494 }
495
496 Instrument::AutoVolume
497 Instrument::auto_volume() const
498 {
499   return m_auto_volume;
500 }
501
502 void
503 Instrument::set_auto_volume (const AutoVolume& new_value)
504 {
505   m_auto_volume = new_value;
506
507   signal_global_changed();
508 }
509
510 Instrument::AutoTune
511 Instrument::auto_tune() const
512 {
513   return m_auto_tune;
514 }
515
516 void
517 Instrument::set_auto_tune (const AutoTune& new_value)
518 {
519   m_auto_tune = new_value;
520
521   signal_global_changed();
522 }
523
524 Instrument::EncoderConfig
525 Instrument::encoder_config() const
526 {
527   return m_encoder_config;
528 }
529
530 void
531 Instrument::set_encoder_config (const EncoderConfig& new_value)
532 {
533   m_encoder_config = new_value;
534
535   signal_global_changed();
536 }