1 /*
2  * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of Zrythm
5  *
6  * Zrythm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Zrythm is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include <stdlib.h>
21 
22 #include "audio/clip.h"
23 #include "audio/encoder.h"
24 #include "audio/engine.h"
25 #include "audio/tempo_track.h"
26 #include "gui/widgets/main_window.h"
27 #include "project.h"
28 #include "utils/audio.h"
29 #include "utils/debug.h"
30 #include "utils/dsp.h"
31 #include "utils/file.h"
32 #include "utils/flags.h"
33 #include "utils/hash.h"
34 #include "utils/io.h"
35 #include "utils/math.h"
36 #include "utils/objects.h"
37 #include "utils/string.h"
38 #include "zrythm_app.h"
39 
40 #include <gtk/gtk.h>
41 #include <glib/gi18n.h>
42 
43 static AudioClip *
_create()44 _create ()
45 {
46   AudioClip * self = object_new (AudioClip);
47   self->schema_version =
48     AUDIO_CLIP_SCHEMA_VERSION;
49 
50   return self;
51 }
52 
53 /**
54  * Updates the channel caches.
55  *
56  * See @ref AudioClip.ch_frames.
57  *
58  * @param start_from Frames to start from (per
59  *   channel. The previous frames will be kept.
60  */
61 void
audio_clip_update_channel_caches(AudioClip * self,size_t start_from)62 audio_clip_update_channel_caches (
63   AudioClip * self,
64   size_t      start_from)
65 {
66   z_return_if_fail_cmp (
67     self->channels, >, 0);
68   z_return_if_fail_cmp (
69     self->num_frames, >, 0);
70 
71   /* copy the frames to the channel caches */
72   for (unsigned int i = 0; i < self->channels; i++)
73     {
74       self->ch_frames[i] =
75         g_realloc (
76           self->ch_frames[i],
77           sizeof (float) *
78             (size_t) self->num_frames);
79       for (size_t j = start_from;
80            j < (size_t) self->num_frames; j++)
81         {
82           self->ch_frames[i][j] =
83             self->frames[j * self->channels + i];
84         }
85     }
86 }
87 
88 static void
audio_clip_init_from_file(AudioClip * self,const char * full_path)89 audio_clip_init_from_file (
90   AudioClip * self,
91   const char * full_path)
92 {
93   g_return_if_fail (self);
94 
95   self->samplerate =
96     (int) AUDIO_ENGINE->sample_rate;
97   g_return_if_fail (self->samplerate > 0);
98 
99   AudioEncoder * enc =
100     audio_encoder_new_from_file (full_path);
101   audio_encoder_decode (
102     enc, self->samplerate, F_SHOW_PROGRESS);
103 
104   size_t arr_size =
105     (size_t) enc->num_out_frames *
106     (size_t) enc->nfo.channels;
107   self->frames =
108     g_realloc (
109       self->frames, arr_size * sizeof (float));
110   self->num_frames = enc->num_out_frames;
111   dsp_copy (
112     self->frames, enc->out_frames, arr_size);
113   g_free_and_null (self->name);
114   char * basename = g_path_get_basename (full_path);
115   self->name = io_file_strip_ext (basename);
116   g_free (basename);
117   self->channels = enc->nfo.channels;
118   self->bpm =
119     tempo_track_get_current_bpm (P_TEMPO_TRACK);
120   switch (enc->nfo.bit_depth)
121     {
122     case 16:
123       self->bit_depth = BIT_DEPTH_16;
124       self->use_flac = true;
125       break;
126     case 24:
127       self->bit_depth = BIT_DEPTH_24;
128       self->use_flac = true;
129       break;
130     case 32:
131       self->bit_depth = BIT_DEPTH_32;
132       self->use_flac = false;
133       break;
134     default:
135       g_debug (
136         "unknown bit depth: %d", enc->nfo.bit_depth);
137       self->bit_depth = BIT_DEPTH_32;
138       self->use_flac = false;
139     }
140   /*g_message (*/
141     /*"\n\n num frames %ld \n\n", self->num_frames);*/
142   audio_clip_update_channel_caches (self, 0);
143 
144   audio_encoder_free (enc);
145 }
146 
147 /**
148  * Inits after loading a Project.
149  */
150 void
audio_clip_init_loaded(AudioClip * self)151 audio_clip_init_loaded (
152   AudioClip * self)
153 {
154   g_debug (
155     "%s: %p", __func__, self);
156 
157   char * filepath =
158     audio_clip_get_path_in_pool_from_name (
159       self->name, self->use_flac, F_NOT_BACKUP);
160 
161   bpm_t bpm = self->bpm;
162   audio_clip_init_from_file (self, filepath);
163   self->bpm = bpm;
164 
165   g_free (filepath);
166 }
167 
168 /**
169  * Creates an audio clip from a file.
170  *
171  * The name used is the basename of the file.
172  */
173 AudioClip *
audio_clip_new_from_file(const char * full_path)174 audio_clip_new_from_file (
175   const char * full_path)
176 {
177   AudioClip * self = _create ();
178 
179   audio_clip_init_from_file (self, full_path);
180 
181   self->pool_id = -1;
182   self->bpm =
183     tempo_track_get_current_bpm (P_TEMPO_TRACK);
184 
185   return self;
186 }
187 
188 /**
189  * Creates an audio clip by copying the given float
190  * array.
191  *
192  * @param name A name for this clip.
193  */
194 AudioClip *
audio_clip_new_from_float_array(const float * arr,const long nframes,const channels_t channels,BitDepth bit_depth,const char * name)195 audio_clip_new_from_float_array (
196   const float *    arr,
197   const long       nframes,
198   const channels_t channels,
199   BitDepth         bit_depth,
200   const char *     name)
201 {
202   AudioClip * self = _create ();
203 
204   self->frames =
205     object_new_n (
206       (size_t) (nframes * (long) channels),
207       sample_t);
208   self->num_frames = nframes;
209   self->channels = channels;
210   self->samplerate = (int) AUDIO_ENGINE->sample_rate;
211   g_return_val_if_fail (self->samplerate > 0, NULL);
212   self->name = g_strdup (name);
213   self->bit_depth = bit_depth;
214   self->use_flac = bit_depth < BIT_DEPTH_32;
215   self->pool_id = -1;
216   dsp_copy (
217     self->frames, arr,
218     (size_t) nframes * (size_t) channels);
219   self->bpm =
220     tempo_track_get_current_bpm (P_TEMPO_TRACK);
221   audio_clip_update_channel_caches (self, 0);
222 
223   return self;
224 }
225 
226 /**
227  * Create an audio clip while recording.
228  *
229  * The frames will keep getting reallocated until
230  * the recording is finished.
231  *
232  * @param nframes Number of frames to allocate. This
233  *   should be the current cycle's frames when
234  *   called during recording.
235  */
236 AudioClip *
audio_clip_new_recording(const channels_t channels,const long nframes,const char * name)237 audio_clip_new_recording (
238   const channels_t channels,
239   const long       nframes,
240   const char *     name)
241 {
242   AudioClip * self = _create ();
243 
244   self->channels = channels;
245   self->frames =
246     object_new_n (
247       (size_t) (nframes * (long) self->channels),
248       sample_t);
249   self->num_frames = nframes;
250   self->name = g_strdup (name);
251   self->pool_id = -1;
252   self->bpm =
253     tempo_track_get_current_bpm (P_TEMPO_TRACK);
254   self->samplerate = (int) AUDIO_ENGINE->sample_rate;
255   self->bit_depth = BIT_DEPTH_32;
256   self->use_flac = false;
257   g_return_val_if_fail (self->samplerate > 0, NULL);
258   dsp_fill (
259     self->frames, DENORMAL_PREVENTION_VAL,
260     (size_t) nframes * (size_t) channels);
261   audio_clip_update_channel_caches (self, 0);
262 
263   return self;
264 }
265 
266 /**
267  * Gets the path of a clip matching \ref name from
268  * the pool.
269  *
270  * @param use_flac Whether to look for a FLAC file
271  *   instead of a wav file.
272  * @param is_backup Whether writing to a backup
273  *   project.
274  */
275 char *
audio_clip_get_path_in_pool_from_name(const char * name,bool use_flac,bool is_backup)276 audio_clip_get_path_in_pool_from_name (
277   const char * name,
278   bool         use_flac,
279   bool         is_backup)
280 {
281   char * prj_pool_dir =
282     project_get_path (
283       PROJECT, PROJECT_PATH_POOL, is_backup);
284   if (!file_exists (prj_pool_dir))
285     {
286       g_critical ("%s does not exist", prj_pool_dir);
287       return  NULL;
288     }
289   char * without_ext =
290     io_file_strip_ext (name);
291   char * basename =
292     g_strdup_printf (
293       "%s.%s", without_ext,
294       use_flac ? "FLAC" : "wav");
295   char * new_path =
296     g_build_filename (
297       prj_pool_dir, basename, NULL);
298   g_free (without_ext);
299   g_free (basename);
300   g_free (prj_pool_dir);
301 
302   return new_path;
303 }
304 
305 /**
306  * Gets the path of the given clip from the pool.
307  *
308  * @param is_backup Whether writing to a backup
309  *   project.
310  */
311 char *
audio_clip_get_path_in_pool(AudioClip * self,bool is_backup)312 audio_clip_get_path_in_pool (
313   AudioClip * self,
314   bool        is_backup)
315 {
316   return
317     audio_clip_get_path_in_pool_from_name (
318       self->name, self->use_flac, is_backup);
319 }
320 
321 /**
322  * Writes the clip to the pool as a wav file.
323  *
324  * @param parts If true, only write new data. @see
325  *   AudioClip.frames_written.
326  * @param is_backup Whether writing to a backup
327  *   project.
328  */
329 void
audio_clip_write_to_pool(AudioClip * self,bool parts,bool is_backup)330 audio_clip_write_to_pool (
331   AudioClip * self,
332   bool        parts,
333   bool        is_backup)
334 {
335   AudioClip * pool_clip =
336     audio_pool_get_clip (
337       AUDIO_POOL, self->pool_id);
338   g_return_if_fail (pool_clip);
339   g_return_if_fail (pool_clip == self);
340 
341   audio_pool_print (AUDIO_POOL);
342   g_message (
343     "attempting to write clip %s (%d) to pool...",
344     self->name, self->pool_id);
345 
346   /* generate a copy of the given filename in the
347    * project dir */
348   char * path_in_main_project =
349     audio_clip_get_path_in_pool (
350       self, F_NOT_BACKUP);
351   char * new_path =
352     audio_clip_get_path_in_pool (self, is_backup);
353   g_return_if_fail (path_in_main_project);
354   g_return_if_fail (new_path);
355 
356   /* whether a new write is needed */
357   bool need_new_write = true;
358 
359   /* skip if file with same hash already exists */
360   if (file_exists (new_path) && !parts)
361     {
362       char * existing_file_hash =
363         hash_get_from_file (
364           new_path, HASH_ALGORITHM_XXH3_64);
365       bool same_hash =
366         self->file_hash &&
367         string_is_equal (
368           self->file_hash, existing_file_hash);
369       g_free (existing_file_hash);
370 
371       if (same_hash)
372         {
373           g_debug (
374             "skipping writing to existing clip %s "
375             "in pool",
376             new_path);
377           need_new_write = false;
378         }
379 #if 0
380       else
381         {
382           g_critical (
383             "attempted to overwrite %s with a "
384             "different clip",
385             new_path);
386           return;
387         }
388 #endif
389     }
390 
391   /* if writing to backup and same file exists in
392    * main project dir, copy (first try reflink) */
393   if (need_new_write && self->file_hash && is_backup)
394     {
395       bool exists_in_main_project = false;
396       if (file_exists (path_in_main_project))
397         {
398           char * existing_file_hash =
399             hash_get_from_file (
400               path_in_main_project,
401               HASH_ALGORITHM_XXH3_64);
402           exists_in_main_project =
403             string_is_equal (
404               self->file_hash, existing_file_hash);
405           g_free (existing_file_hash);
406         }
407 
408       if (exists_in_main_project)
409         {
410           /* try reflink */
411           g_debug (
412             "reflinking clip from main project "
413             "('%s' to '%s')",
414             path_in_main_project, new_path);
415 
416           if (file_reflink (
417                 path_in_main_project, new_path) != 0)
418             {
419               g_message (
420                 "failed to reflink, copying "
421                 "instead");
422 
423               /* copy */
424               GFile * src_file =
425                 g_file_new_for_path (
426                   path_in_main_project);
427               GFile * dest_file =
428                 g_file_new_for_path (new_path);
429               GError * err = NULL;
430               g_debug (
431                 "copying clip from main project "
432                 "('%s' to '%s')",
433                 path_in_main_project, new_path);
434               if (g_file_copy (
435                     src_file, dest_file, 0, NULL, NULL,
436                     NULL, &err))
437                 {
438                   need_new_write = false;
439                 }
440               else /* else if failed */
441                 {
442                   g_warning (
443                     "Failed to copy '%s' to '%s': %s",
444                     path_in_main_project, new_path,
445                     err->message);
446                 }
447             } /* endif reflink fail */
448         }
449     }
450 
451   if (need_new_write)
452     {
453       g_debug (
454         "writing clip %s to pool "
455         "(parts %d, is backup  %d): '%s'",
456         self->name, parts, is_backup, new_path);
457       audio_clip_write_to_file (
458         self, new_path, parts);
459 
460       if (!parts)
461         {
462           /* store file hash */
463           g_free_and_null (self->file_hash);
464           self->file_hash =
465             hash_get_from_file (
466               new_path, HASH_ALGORITHM_XXH3_64);
467         }
468     }
469 
470   g_free (path_in_main_project);
471   g_free (new_path);
472 
473   audio_pool_print (AUDIO_POOL);
474 }
475 
476 /**
477  * Writes the given audio clip data to a file.
478  *
479  * @param parts If true, only write new data. @see
480  *   AudioClip.frames_written.
481  *
482  * @return Non-zero if fail.
483  */
484 int
audio_clip_write_to_file(AudioClip * self,const char * filepath,bool parts)485 audio_clip_write_to_file (
486   AudioClip *  self,
487   const char * filepath,
488   bool         parts)
489 {
490   g_return_val_if_fail (self->samplerate > 0, -1);
491   size_t before_frames =
492     (size_t) self->frames_written;
493   long ch_offset =
494     parts ? self->frames_written : 0;
495   long offset = ch_offset * self->channels;
496   int ret =
497     audio_write_raw_file (
498       &self->frames[offset], ch_offset,
499       parts ?
500         (self->num_frames - self->frames_written) :
501         self->num_frames,
502       (uint32_t) self->samplerate,
503       self->use_flac, self->bit_depth,
504       self->channels, filepath);
505   audio_clip_update_channel_caches (
506     self, before_frames);
507 
508   if (parts && ret == 0)
509     {
510       self->frames_written = self->num_frames;
511       self->last_write = g_get_monotonic_time ();
512     }
513 
514   /* TODO move this to a unit test for this
515    * function */
516   if (ZRYTHM_TESTING)
517     {
518       AudioClip * new_clip =
519         audio_clip_new_from_file (filepath);
520       if (self->num_frames != new_clip->num_frames)
521         {
522           g_warning (
523             "%zu != %zu",
524             self->num_frames, new_clip->num_frames);
525         }
526       float epsilon = 0.0001f;
527       g_warn_if_fail (
528         audio_frames_equal (
529          self->ch_frames[0], new_clip->ch_frames[0],
530          (size_t) new_clip->num_frames,
531          epsilon));
532       g_warn_if_fail (
533         audio_frames_equal (
534          self->frames, new_clip->frames,
535          (size_t)
536          new_clip->num_frames * new_clip->channels,
537          epsilon));
538       audio_clip_free (new_clip);
539     }
540 
541   return ret;
542 }
543 
544 /**
545  * Returns whether the clip is used inside the
546  * project.
547  *
548  * @param check_undo_stack If true, this checks both
549  *   project regions and the undo stack. If false,
550  *   this only checks actual project regions only.
551  */
552 bool
audio_clip_is_in_use(AudioClip * self,bool check_undo_stack)553 audio_clip_is_in_use (
554   AudioClip * self,
555   bool        check_undo_stack)
556 {
557   for (int i = 0; i < TRACKLIST->num_tracks; i++)
558     {
559       Track * track = TRACKLIST->tracks[i];
560       if (track->type != TRACK_TYPE_AUDIO)
561         continue;
562 
563       for (int j = 0; j < track->num_lanes; j++)
564         {
565           TrackLane * lane = track->lanes[j];
566 
567           for (int k = 0; k < lane->num_regions; k++)
568             {
569               ZRegion * r = lane->regions[k];
570               if (r->id.type != REGION_TYPE_AUDIO)
571                 continue;
572 
573               if (r->pool_id == self->pool_id)
574                 return true;
575             }
576         }
577     }
578 
579   if (check_undo_stack)
580     {
581       if (undo_manager_contains_clip (
582             UNDO_MANAGER, self))
583         {
584           return true;
585         }
586     }
587 
588   return false;
589 }
590 
591 typedef struct AppLaunchData
592 {
593   GFile *         file;
594   GtkAppChooser * app_chooser;
595 } AppLaunchData;
596 
597 static void
app_launch_data_free(AppLaunchData * data,GClosure * closure)598 app_launch_data_free (
599   AppLaunchData * data,
600   GClosure *      closure)
601 {
602   free (data);
603 }
604 
605 static void
on_launch_clicked(GtkButton * btn,AppLaunchData * data)606 on_launch_clicked (
607   GtkButton *     btn,
608   AppLaunchData * data)
609 {
610   GError * err = NULL;
611   GList * file_list = NULL;
612   file_list =
613     g_list_append (file_list, data->file);
614   GAppInfo * app_nfo =
615     gtk_app_chooser_get_app_info (data->app_chooser);
616   bool success =
617     g_app_info_launch (
618       app_nfo, file_list, NULL, &err);
619   g_list_free (file_list);
620   if (!success)
621     {
622       g_message ("app launch unsuccessful");
623     }
624 }
625 
626 /**
627  * Shows a dialog with info on how to edit a file,
628  * with an option to open an app launcher.
629  *
630  * When the user closes the dialog, the clip is
631  * assumed to have been edited.
632  *
633  * The given audio clip will be free'd.
634  *
635  * @note This must not be used on pool clips.
636  *
637  * @return A new instance of AudioClip if successful,
638  *   NULL, if not.
639  */
640 AudioClip *
audio_clip_edit_in_ext_program(AudioClip * self)641 audio_clip_edit_in_ext_program (
642   AudioClip * self)
643 {
644   GError * err = NULL;
645   char * tmp_dir =
646     g_dir_make_tmp (
647       "zrythm-audio-clip-tmp-XXXXXX", &err);
648   g_return_val_if_fail (tmp_dir, NULL);
649   char * abs_path =
650     g_build_filename (
651       tmp_dir, "tmp.wav", NULL);
652   audio_clip_write_to_file (
653     self, abs_path, false);
654   audio_clip_free (self);
655 
656   GFile * file =
657     g_file_new_for_path (abs_path);
658   err = NULL;
659   GFileInfo * file_info =
660     g_file_query_info (
661       file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
662       G_FILE_QUERY_INFO_NONE, NULL, &err);
663   g_return_val_if_fail (file_info, false);
664   const char * content_type =
665     g_file_info_get_content_type (file_info);
666 
667   GtkWidget * dialog =
668     gtk_dialog_new_with_buttons (
669       _("Edit in external app"),
670       GTK_WINDOW (MAIN_WINDOW),
671       GTK_DIALOG_MODAL
672       | GTK_DIALOG_DESTROY_WITH_PARENT,
673       _("_OK"), GTK_RESPONSE_ACCEPT,
674       _("_Cancel"), GTK_RESPONSE_REJECT,
675       NULL);
676 
677   /* populate content area */
678   GtkWidget * content_area =
679     gtk_dialog_get_content_area (
680       GTK_DIALOG (dialog));
681   GtkWidget * main_box =
682     gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
683   gtk_widget_set_margin_start (main_box, 4);
684   gtk_widget_set_margin_end (main_box, 4);
685   gtk_widget_set_margin_top (main_box, 4);
686   gtk_widget_set_margin_bottom (main_box, 4);
687   gtk_container_add (
688     GTK_CONTAINER (content_area), main_box);
689 
690   GtkWidget * lbl = gtk_label_new ("");
691   gtk_label_set_selectable (GTK_LABEL (lbl), true);
692   char * markup =
693     g_markup_printf_escaped (
694       _("Edit the file at <u>%s</u>, then "
695       "press OK"),
696       abs_path);
697   gtk_label_set_markup (
698     GTK_LABEL (lbl), markup);
699   gtk_container_add (GTK_CONTAINER (main_box), lbl);
700 
701   GtkWidget * launch_box =
702     gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
703   gtk_widget_set_halign (
704     launch_box, GTK_ALIGN_CENTER);
705   GtkWidget * app_chooser_button =
706     gtk_app_chooser_button_new (content_type);
707   gtk_container_add (
708     GTK_CONTAINER (launch_box), app_chooser_button);
709   GtkWidget * btn =
710     gtk_button_new_with_label (_("Launch"));
711   AppLaunchData * data = object_new (AppLaunchData);
712   data->file = file;
713   data->app_chooser =
714     GTK_APP_CHOOSER (app_chooser_button);
715   g_signal_connect_data (
716     G_OBJECT (btn), "clicked",
717     G_CALLBACK (on_launch_clicked), data,
718     (GClosureNotify) app_launch_data_free, 0);
719   gtk_container_add (
720     GTK_CONTAINER (launch_box), btn);
721   gtk_container_add (
722     GTK_CONTAINER (main_box), launch_box);
723 
724   gtk_widget_show_all (content_area);
725   int ret =
726     gtk_dialog_run (GTK_DIALOG (dialog));
727   gtk_widget_destroy (GTK_WIDGET (dialog));
728   if (ret != GTK_RESPONSE_ACCEPT)
729     {
730       g_debug ("cancelled");
731       return NULL;
732     }
733 
734   /* ok - reload from file */
735   self = audio_clip_new_from_file (abs_path);
736   g_return_val_if_fail (self, false);
737 
738   return self;
739 }
740 
741 /**
742  * To be called by audio_pool_remove_clip().
743  *
744  * Removes the file associated with the clip and
745  * frees the instance.
746  *
747  * @param backup Whether to remove from backup
748  *   directory.
749  */
750 void
audio_clip_remove_and_free(AudioClip * self,bool backup)751 audio_clip_remove_and_free (
752   AudioClip * self,
753   bool        backup)
754 {
755   char * path =
756     audio_clip_get_path_in_pool (
757       self, F_NOT_BACKUP);
758   g_message ("removing clip at %s", path);
759   g_return_if_fail (path);
760   io_remove (path);
761 
762   audio_clip_free (self);
763 }
764 
765 AudioClip *
audio_clip_clone(AudioClip * src)766 audio_clip_clone (
767   AudioClip * src)
768 {
769   AudioClip * self = object_new (AudioClip);
770   self->schema_version = AUDIO_CLIP_SCHEMA_VERSION;
771 
772   self->name = g_strdup (src->name);
773   self->file_hash = g_strdup (src->file_hash);
774   self->bpm = src->bpm;
775   self->bit_depth = src->bit_depth;
776   self->use_flac = src->use_flac;
777   self->samplerate = src->samplerate;
778   self->pool_id = src->pool_id;
779 
780   return self;
781 }
782 
783 /**
784  * Frees the audio clip.
785  */
786 void
audio_clip_free(AudioClip * self)787 audio_clip_free (
788   AudioClip * self)
789 {
790   object_zero_and_free (self->frames);
791   for (unsigned int i = 0; i < self->channels; i++)
792     {
793       object_zero_and_free_if_nonnull (
794         self->ch_frames[i]);
795     }
796   g_free_and_null (self->name);
797   g_free_and_null (self->file_hash);
798 
799   object_zero_and_free (self);
800 }
801