Browse Source

add pitch shift audio function

Implements https://todo.sr.ht/~alextee/zrythm-feature/585.
translate
Alexandros Theodotou 2 months ago
parent
commit
0799e098fa
Signed by: alex
GPG Key ID: 022EAE42313D70F3
  1. 2
      inc/actions/arranger_selections.h
  2. 35
      inc/audio/audio_function.h
  3. 39
      inc/audio/stretcher.h
  4. 20
      inc/gui/widgets/dialogs/string_entry_dialog.h
  5. 2
      inc/utils/objects.h
  6. 10
      inc/utils/types.h
  7. 5
      scripts/gen-schema.scm.in
  8. 102
      src/actions/actions.c
  9. 17
      src/actions/arranger_selections.c
  10. 170
      src/audio/audio_function.c
  11. 1
      src/audio/audio_region.c
  12. 16
      src/audio/stretcher.c
  13. 11
      tests/actions/arranger_selections.c

2
inc/actions/arranger_selections.h

@ -582,6 +582,7 @@ WARN_UNUSED_RESULT UndoableAction * @@ -582,6 +582,7 @@ WARN_UNUSED_RESULT UndoableAction *
arranger_selections_action_new_edit_audio_function (
ArrangerSelections * sel_before,
AudioFunctionType audio_func_type,
AudioFunctionOpts opts,
const char * uri,
GError ** error);
@ -789,6 +790,7 @@ bool @@ -789,6 +790,7 @@ bool
arranger_selections_action_perform_edit_audio_function (
ArrangerSelections * sel_before,
AudioFunctionType audio_func_type,
AudioFunctionOpts opts,
const char * uri,
GError ** error);

35
inc/audio/audio_function.h

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: © 2020-2022 Alexandros Theodotou <alex@zrythm.org>
// SPDX-FileCopyrightText: © 2020-2023 Alexandros Theodotou <alex@zrythm.org>
// SPDX-License-Identifier: LicenseRef-ZrythmLicense
/**
@ -34,6 +34,7 @@ typedef enum AudioFunctionType @@ -34,6 +34,7 @@ typedef enum AudioFunctionType
AUDIO_FUNCTION_NUDGE_LEFT,
AUDIO_FUNCTION_NUDGE_RIGHT,
AUDIO_FUNCTION_REVERSE,
AUDIO_FUNCTION_PITCH_SHIFT,
/** External program. */
AUDIO_FUNCTION_EXT_PROGRAM,
@ -58,12 +59,22 @@ static const cyaml_strval_t audio_function_type_strings[] = { @@ -58,12 +59,22 @@ static const cyaml_strval_t audio_function_type_strings[] = {
{ __ ("Nudge left"), AUDIO_FUNCTION_NUDGE_LEFT },
{ __ ("Nudge right"), AUDIO_FUNCTION_NUDGE_RIGHT },
{ __ ("Reverse"), AUDIO_FUNCTION_REVERSE },
{ __ ("Pitch shift"), AUDIO_FUNCTION_PITCH_SHIFT },
{ __ ("External program"), AUDIO_FUNCTION_EXT_PROGRAM },
{ __ ("Guile script"), AUDIO_FUNCTION_GUILE_SCRIPT },
{ __ ("Custom plugin"), AUDIO_FUNCTION_CUSTOM_PLUGIN },
{ __ ("Invalid"), AUDIO_FUNCTION_INVALID },
};
typedef struct AudioFunctionOpts
{
/**
* Amount related to the current function (e.g. pitch shift).
*/
double amount;
} AudioFunctionOpts;
static inline const char *
audio_function_type_to_string (AudioFunctionType type)
{
@ -94,8 +105,8 @@ const char * @@ -94,8 +105,8 @@ const char *
audio_function_get_icon_name_for_type (AudioFunctionType type);
/**
* Returns the URI of the plugin responsible for
* handling the type, if any.
* Returns the URI of the plugin responsible for handling the
* type, if any.
*/
static inline const char *
audio_function_get_plugin_uri_for_type (AudioFunctionType type)
@ -112,22 +123,22 @@ audio_function_get_plugin_uri_for_type (AudioFunctionType type) @@ -112,22 +123,22 @@ audio_function_get_plugin_uri_for_type (AudioFunctionType type)
/**
* Applies the given action to the given selections.
*
* This will save a file in the pool and store the
* pool ID in the selections.
* This will save a file in the pool and store the pool ID in
* the selections.
*
* @param sel Selections to edit.
* @param type Function type. If invalid is passed,
* this will simply add the audio file in the pool
* for the unchanged audio material (used in
* audio selection actions for the selections
* before the change).
* @param type Function type. If invalid is passed, this will
* simply add the audio file in the pool for the unchanged
* audio material (used in audio selection actions for the
* selections before the change).
*
* @return Non-zero if error.
* @return Whether successful.
*/
int
bool
audio_function_apply (
ArrangerSelections * sel,
AudioFunctionType type,
AudioFunctionOpts opts,
const char * uri,
GError ** error);

39
inc/audio/stretcher.h

@ -1,21 +1,5 @@ @@ -1,21 +1,5 @@
/*
* Copyright (C) 2019-2020 Alexandros Theodotou <alex at zrythm dot org>
*
* This file is part of Zrythm
*
* Zrythm is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Zrythm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
*/
// SPDX-FileCopyrightText: © 2019-2020 Alexandros Theodotou <alex@zrythm.org>
// SPDX-License-Identifier: LicenseRef-ZrythmLicense
/**
* \file
@ -69,8 +53,7 @@ typedef struct Stretcher @@ -69,8 +53,7 @@ typedef struct Stretcher
bool is_realtime;
/**
* Size of the block to process in each
* iteration.
* Size of the block to process in each iteration.
*
* Somewhere around 6k should be fine.
*/
@ -78,17 +61,15 @@ typedef struct Stretcher @@ -78,17 +61,15 @@ typedef struct Stretcher
} Stretcher;
/**
* Create a new Stretcher using the rubberband
* backend.
* Create a new Stretcher using the rubberband backend.
*
* @param samplerate The new samplerate.
* @param time_ratio The ratio to multiply time by
* (eg if the BPM is doubled, this will be 0.5).
* @param pitch_ratio The ratio to pitch by. This
* will normally be 1.0 when time-stretching).
* @param realtime Whether to perform realtime
* stretching (lower quality but fast enough to
* be used real-time).
* @param time_ratio The ratio to multiply time by (eg if the
* BPM is doubled, this will be 0.5).
* @param pitch_ratio The ratio to pitch by. This will normally
* be 1.0 when time-stretching).
* @param realtime Whether to perform realtime stretching
* (lower quality but fast enough to be used real-time).
*/
Stretcher *
stretcher_new_rubberband (

20
inc/gui/widgets/dialogs/string_entry_dialog.h

@ -1,21 +1,5 @@ @@ -1,21 +1,5 @@
/*
* Copyright (C) 2019-2020 Alexandros Theodotou <alex at zrythm dot org>
*
* This file is part of Zrythm
*
* Zrythm is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Zrythm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// SPDX-FileCopyrightText: © 2019-2020 Alexandros Theodotou <alex@zrythm.org>
// SPDX-License-Identifier: LicenseRef-ZrythmLicense
/**
* \file

2
inc/utils/objects.h

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: © 2019-2022 Alexandros Theodotou <alex@zrythm.org>
// SPDX-FileCopyrightText: © 2019-2023 Alexandros Theodotou <alex@zrythm.org>
// SPDX-License-Identifier: LicenseRef-ZrythmLicense
#ifndef __UTILS_OBJECTS_H__

10
inc/utils/types.h

@ -82,17 +82,17 @@ typedef void (*GenericFloatSetter) (void * object, float val); @@ -82,17 +82,17 @@ typedef void (*GenericFloatSetter) (void * object, float val);
typedef const char * (*GenericStringGetter) (void * object);
/**
* Getter prototype for strings to be saved in the
* given buffer.
* Setter prototype for float values.
*/
typedef void (
*GenericStringCopyGetter) (void * object, char * buf);
*GenericStringSetter) (void * object, const char * val);
/**
* Setter prototype for float values.
* Getter prototype for strings to be saved in the
* given buffer.
*/
typedef void (
*GenericStringSetter) (void * object, const char * val);
*GenericStringCopyGetter) (void * object, char * buf);
/**
* Generic callback.

5
scripts/gen-schema.scm.in

@ -585,6 +585,11 @@ Args: @@ -585,6 +585,11 @@ Args:
"audio-function" "i" "0"
"Last used audio function"
"Last used audio function (index corresponding to enum in audio function action).")
(make-schema-key-with-range
"audio-function-pitch-shift-ratio" "d"
"0.001" "100.0" "1.0"
"Last used pitch shift ratio"
"Last used pitch shift ratio.")
(make-schema-key
"timeline-playhead-scroll-edges" "b"
"true"

102
src/actions/actions.c

@ -109,6 +109,7 @@ @@ -109,6 +109,7 @@
#include "utils/localization.h"
#include "utils/log.h"
#include "utils/math.h"
#include "utils/objects.h"
#include "utils/progress_info.h"
#include "utils/resources.h"
#include "utils/stack.h"
@ -2451,7 +2452,10 @@ do_automation_func (AutomationFunctionType type) @@ -2451,7 +2452,10 @@ do_automation_func (AutomationFunctionType type)
* @param uri Plugin URI, if applying plugin.
*/
static void
do_audio_func (const AudioFunctionType type, const char * uri)
do_audio_func (
const AudioFunctionType type,
const AudioFunctionOpts opts,
const char * uri)
{
g_return_if_fail (region_find (&CLIP_EDITOR->region_id));
AUDIO_SELECTIONS->region_id = CLIP_EDITOR->region_id;
@ -2475,11 +2479,11 @@ do_audio_func (const AudioFunctionType type, const char * uri) @@ -2475,11 +2479,11 @@ do_audio_func (const AudioFunctionType type, const char * uri)
zix_sem_wait (&PROJECT->save_sem);
ret = arranger_selections_action_perform_edit_audio_function (
sel, type, uri, &err);
sel, type, opts, uri, &err);
if (!ret)
{
HANDLE_ERROR (
err, "%s", _ ("Failed to apply automation function"));
err, "%s", _ ("Failed to apply audio function"));
}
zix_sem_post (&PROJECT->save_sem);
@ -2488,6 +2492,37 @@ free_audio_sel_and_return: @@ -2488,6 +2492,37 @@ free_audio_sel_and_return:
arranger_selections_free (sel);
}
static void
set_pitch_ratio (void * object, const char * ratio_str)
{
double ratio = strtod (ratio_str, NULL);
if (ratio < 0.0001 || ratio > 100.0)
{
ui_show_error_message (
false, _ ("Please enter a valid ratio."));
return;
}
AudioFunctionOpts opts;
opts.amount = ratio;
do_audio_func (AUDIO_FUNCTION_PITCH_SHIFT, opts, NULL);
}
static const char *
get_pitch_ratio (void * object)
{
static char * pitch_ratio = NULL;
if (pitch_ratio == NULL)
{
g_free_and_null (pitch_ratio);
}
pitch_ratio = g_strdup_printf (
"%f",
g_settings_get_double (
S_UI, "audio-function-pitch-shift-ratio"));
return pitch_ratio;
}
DEFINE_SIMPLE (activate_editor_function)
{
size_t size;
@ -2572,19 +2607,48 @@ DEFINE_SIMPLE (activate_editor_function) @@ -2572,19 +2607,48 @@ DEFINE_SIMPLE (activate_editor_function)
bool done = false;
if (string_is_equal (str, "current"))
{
do_audio_func (
g_settings_get_int (S_UI, "audio-function"),
NULL);
AudioFunctionOpts opts;
AudioFunctionType type =
g_settings_get_int (S_UI, "audio-function");
switch (type)
{
case AUDIO_FUNCTION_PITCH_SHIFT:
opts.amount = g_settings_get_double (
S_UI, "audio-function-pitch-shift-ratio");
break;
default:
break;
}
do_audio_func (type, opts, NULL);
done = true;
}
for (int i = AUDIO_FUNCTION_INVERT;
for (AudioFunctionType i = AUDIO_FUNCTION_INVERT;
i < AUDIO_FUNCTION_CUSTOM_PLUGIN; i++)
{
char * audio_func_target =
audio_function_get_action_target_for_type (i);
if (string_is_equal (str, audio_func_target))
do_audio_func (i, NULL);
{
switch (i)
{
case AUDIO_FUNCTION_PITCH_SHIFT:
{
StringEntryDialogWidget * dialog =
string_entry_dialog_widget_new (
_ ("Pitch Ratio"), NULL,
(GenericStringGetter) get_pitch_ratio,
(GenericStringSetter) set_pitch_ratio);
gtk_window_present (GTK_WINDOW (dialog));
g_free (audio_func_target);
}
return;
default:
AudioFunctionOpts opts = {};
do_audio_func (i, opts, NULL);
break;
}
}
g_free (audio_func_target);
done = true;
}
@ -2618,7 +2682,9 @@ DEFINE_SIMPLE (activate_editor_function_lv2) @@ -2618,7 +2682,9 @@ DEFINE_SIMPLE (activate_editor_function_lv2)
break;
case REGION_TYPE_AUDIO:
{
do_audio_func (AUDIO_FUNCTION_CUSTOM_PLUGIN, str);
AudioFunctionOpts opts = {};
do_audio_func (
AUDIO_FUNCTION_CUSTOM_PLUGIN, opts, str);
}
break;
default:
@ -2897,10 +2963,11 @@ DEFINE_SIMPLE (activate_nudge_selection) @@ -2897,10 +2963,11 @@ DEFINE_SIMPLE (activate_nudge_selection)
if (sel->type == ARRANGER_SELECTIONS_TYPE_AUDIO)
{
AudioFunctionOpts opts = {};
do_audio_func (
left ? AUDIO_FUNCTION_NUDGE_LEFT
: AUDIO_FUNCTION_NUDGE_RIGHT,
NULL);
opts, NULL);
return;
}
@ -2954,6 +3021,19 @@ DEFINE_SIMPLE (activate_timeline_function) @@ -2954,6 +3021,19 @@ DEFINE_SIMPLE (activate_timeline_function)
{
AudioFunctionType type =
(AudioFunctionType) g_variant_get_int32 (variant);
AudioFunctionOpts opts;
/* FIXME need to show a dialog here too to ask for the
* pitch shift amount
* TODO refactor code to avoid duplicating the code here */
switch (type)
{
case AUDIO_FUNCTION_PITCH_SHIFT:
opts.amount = g_settings_get_double (
S_UI, "audio-function-pitch-shift-ratio");
break;
default:
break;
}
for (int i = 0; i < TL_SELECTIONS->num_regions; i++)
{
@ -2982,7 +3062,7 @@ DEFINE_SIMPLE (activate_timeline_function) @@ -2982,7 +3062,7 @@ DEFINE_SIMPLE (activate_timeline_function)
GError * err = NULL;
bool ret =
arranger_selections_action_perform_edit_audio_function (
(ArrangerSelections *) sel, type, NULL, &err);
(ArrangerSelections *) sel, type, opts, NULL, &err);
if (!ret)
{
HANDLE_ERROR (

17
src/actions/arranger_selections.c

@ -620,6 +620,7 @@ UndoableAction * @@ -620,6 +620,7 @@ UndoableAction *
arranger_selections_action_new_edit_audio_function (
ArrangerSelections * sel_before,
AudioFunctionType audio_func_type,
AudioFunctionOpts opts,
const char * uri,
GError ** error)
{
@ -629,9 +630,10 @@ arranger_selections_action_new_edit_audio_function ( @@ -629,9 +630,10 @@ arranger_selections_action_new_edit_audio_function (
g_debug ("saving file before applying audio func...");
GError * err = NULL;
int ret = audio_function_apply (
sel_before_clone, AUDIO_FUNCTION_INVALID, NULL, &err);
if (ret != 0)
bool success = audio_function_apply (
sel_before_clone, AUDIO_FUNCTION_INVALID, opts, NULL,
&err);
if (!success)
{
PROPAGATE_PREFIXED_ERROR (
error, err, "%s",
@ -643,9 +645,9 @@ arranger_selections_action_new_edit_audio_function ( @@ -643,9 +645,9 @@ arranger_selections_action_new_edit_audio_function (
ArrangerSelections * sel_after =
arranger_selections_clone (sel_before);
g_debug ("applying actual audio func...");
ret = audio_function_apply (
sel_after, audio_func_type, uri, &err);
if (ret != 0)
success = audio_function_apply (
sel_after, audio_func_type, opts, uri, &err);
if (!success)
{
PROPAGATE_PREFIXED_ERROR (
error, err, "%s",
@ -1068,12 +1070,13 @@ bool @@ -1068,12 +1070,13 @@ bool
arranger_selections_action_perform_edit_audio_function (
ArrangerSelections * sel_before,
AudioFunctionType audio_func_type,
AudioFunctionOpts opts,
const char * uri,
GError ** error)
{
UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
arranger_selections_action_new_edit_audio_function, error,
sel_before, audio_func_type, uri, error);
sel_before, audio_func_type, opts, uri, error);
}
bool

170
src/audio/audio_function.c

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: © 2020-2022 Alexandros Theodotou <alex@zrythm.org>
// SPDX-FileCopyrightText: © 2020-2023 Alexandros Theodotou <alex@zrythm.org>
// SPDX-License-Identifier: LicenseRef-ZrythmLicense
#include <inttypes.h>
@ -27,6 +27,8 @@ @@ -27,6 +27,8 @@
#include <glib/gi18n.h>
#include <rubberband/rubberband-c.h>
typedef enum
{
Z_AUDIO_AUDIO_FUNCTION_ERROR_INVALID_POSITIONS,
@ -83,6 +85,8 @@ audio_function_get_icon_name_for_type (AudioFunctionType type) @@ -83,6 +85,8 @@ audio_function_get_icon_name_for_type (AudioFunctionType type)
return "edit-select-invert";
case AUDIO_FUNCTION_REVERSE:
return "path-reverse";
case AUDIO_FUNCTION_PITCH_SHIFT:
return "path-reverse";
case AUDIO_FUNCTION_NORMALIZE_PEAK:
return "kt-set-max-upload-speed";
case AUDIO_FUNCTION_LINEAR_FADE_IN:
@ -319,22 +323,22 @@ apply_plugin ( @@ -319,22 +323,22 @@ apply_plugin (
/**
* Applies the given action to the given selections.
*
* This will save a file in the pool and store the
* pool ID in the selections.
* This will save a file in the pool and store the pool ID in
* the selections.
*
* @param sel Selections to edit.
* @param type Function type. If invalid is passed,
* this will simply add the audio file in the pool
* for the unchanged audio material (used in
* audio selection actions for the selections
* before the change).
* @param type Function type. If invalid is passed, this will
* simply add the audio file in the pool for the unchanged
* audio material (used in audio selection actions for the
* selections before the change).
*
* @return Non-zero if error.
* @return Whether successful.
*/
int
bool
audio_function_apply (
ArrangerSelections * sel,
AudioFunctionType type,
AudioFunctionOpts opts,
const char * uri,
GError ** error)
{
@ -344,12 +348,12 @@ audio_function_apply ( @@ -344,12 +348,12 @@ audio_function_apply (
AudioSelections * audio_sel = (AudioSelections *) sel;
ZRegion * r = region_find (&audio_sel->region_id);
g_return_val_if_fail (r, -1);
g_return_val_if_fail (r, false);
Track * tr =
arranger_object_get_track ((ArrangerObject *) r);
g_return_val_if_fail (tr, -1);
g_return_val_if_fail (tr, false);
AudioClip * orig_clip = audio_region_get_clip (r);
g_return_val_if_fail (orig_clip, -1);
g_return_val_if_fail (orig_clip, false);
Position init_pos;
position_init (&init_pos);
@ -364,7 +368,7 @@ audio_function_apply ( @@ -364,7 +368,7 @@ audio_function_apply (
error, Z_AUDIO_AUDIO_FUNCTION_ERROR,
Z_AUDIO_AUDIO_FUNCTION_ERROR_INVALID_POSITIONS,
_ ("Invalid positions - skipping function"));
return -1;
return false;
}
/* adjust the positions */
@ -420,7 +424,7 @@ audio_function_apply ( @@ -420,7 +424,7 @@ audio_function_apply (
", "
"nudge_frames %" PRIu64,
num_frames, nudge_frames);
z_return_val_if_fail_cmp (nudge_frames, >, 0, -1);
z_return_val_if_fail_cmp (nudge_frames, >, 0, false);
switch (type)
{
@ -452,7 +456,7 @@ audio_function_apply ( @@ -452,7 +456,7 @@ audio_function_apply (
num_frames * channels, 0.f);
break;
case AUDIO_FUNCTION_NUDGE_LEFT:
g_return_val_if_fail (num_frames > nudge_frames, -1);
g_return_val_if_fail (num_frames > nudge_frames, false);
num_frames_excl_nudge =
num_frames - (size_t) nudge_frames;
dsp_copy (
@ -464,7 +468,7 @@ audio_function_apply ( @@ -464,7 +468,7 @@ audio_function_apply (
nudge_frames_all_channels);
break;
case AUDIO_FUNCTION_NUDGE_RIGHT:
g_return_val_if_fail (num_frames > nudge_frames, -1);
g_return_val_if_fail (num_frames > nudge_frames, false);
num_frames_excl_nudge =
num_frames - (size_t) nudge_frames;
dsp_copy (
@ -482,6 +486,120 @@ audio_function_apply ( @@ -482,6 +486,120 @@ audio_function_apply (
num_frames);
}
break;
case AUDIO_FUNCTION_PITCH_SHIFT:
{
use_interleaved = false;
RubberBandState rubberband_state;
RubberBandOptions rubberband_opts =
RubberBandOptionProcessOffline
/* use finer engine if rubberband v3 */
#if RUBBERBAND_API_MAJOR_VERSION > 2 \
|| (RUBBERBAND_API_MAJOR_VERSION == 2 && RUBBERBAND_API_MINOR_VERSION >= 7)
| RubberBandOptionEngineFiner
#endif
| RubberBandOptionPitchHighQuality
| RubberBandOptionFormantPreserved
| RubberBandOptionThreadingAlways
| RubberBandOptionChannelsApart;
rubberband_state = rubberband_new (
AUDIO_ENGINE->sample_rate, channels,
rubberband_opts, 1.0, opts.amount);
const size_t max_process_size = 8192;
rubberband_set_debug_level (rubberband_state, 2);
rubberband_set_max_process_size (
rubberband_state, max_process_size);
rubberband_set_expected_input_duration (
rubberband_state, num_frames);
rubberband_study (
rubberband_state,
(const float * const *) ch_src_frames, num_frames,
true);
size_t samples_fed = 0;
size_t frames_read = 0;
while (frames_read < num_frames)
{
unsigned int samples_required = MIN (
num_frames - samples_fed, max_process_size);
/*rubberband_get_samples_required (*/
/*rubberband_state));*/
float * tmp_in_arrays[2] = {
&ch_src_frames[0][samples_fed],
&ch_src_frames[1][samples_fed]
};
samples_fed += samples_required;
g_message (
"samples required: %u (total fed %zu), latency: %u",
samples_required, samples_fed,
rubberband_get_latency (rubberband_state));
if (samples_required > 0)
{
rubberband_process (
rubberband_state,
(const float * const *) tmp_in_arrays,
samples_required, samples_fed == num_frames);
}
for (;;)
{
int avail =
rubberband_available (rubberband_state);
if (avail == 0)
{
g_message ("avail == 0");
break;
}
else if (avail == -1)
{
g_message ("avail == -1");
/* FIXME for some reason rubberband
* skips the last few samples when the
* pitch ratio is < 1.0
* this workaround just keeps the copied
* original samples at the end and should
* be fixed eventually */
frames_read = num_frames;
break;
#if 0
g_set_error_literal (
error, Z_AUDIO_AUDIO_FUNCTION_ERROR,
Z_AUDIO_AUDIO_FUNCTION_ERROR_FAILED,
"rubberband: finished prematurely");
return false;
#endif
}
float * tmp_out_arrays[2] = {
&ch_dest_frames[0][frames_read],
&ch_dest_frames[1][frames_read]
};
size_t retrieved_out_samples =
rubberband_retrieve (
rubberband_state, tmp_out_arrays,
(unsigned int) avail);
if ((int) retrieved_out_samples != avail)
{
g_set_error (
error, Z_AUDIO_AUDIO_FUNCTION_ERROR,
Z_AUDIO_AUDIO_FUNCTION_ERROR_FAILED,
"rubberband: retrieved out samples (%zu) != available samples (%d)",
retrieved_out_samples, avail);
return false;
}
frames_read += retrieved_out_samples;
g_message (
"retrieved out samples %zu, frames read %zu",
retrieved_out_samples, frames_read);
}
}
if (frames_read != num_frames)
{
g_set_error (
error, Z_AUDIO_AUDIO_FUNCTION_ERROR,
Z_AUDIO_AUDIO_FUNCTION_ERROR_FAILED,
"rubberband: expected %zu frames but read %zu",
num_frames, frames_read);
return false;
}
}
break;
case AUDIO_FUNCTION_EXT_PROGRAM:
{
AudioClip * tmp_clip = audio_clip_new_from_float_array (
@ -496,7 +614,7 @@ audio_function_apply ( @@ -496,7 +614,7 @@ audio_function_apply (
PROPAGATE_PREFIXED_ERROR (
error, err, "%s",
_ ("Failed to get audio clip from external program"));
return -1;
return false;
}
dsp_copy (
&dest_frames[0], &tmp_clip->frames[0],
@ -513,7 +631,7 @@ audio_function_apply ( @@ -513,7 +631,7 @@ audio_function_apply (
break;
case AUDIO_FUNCTION_CUSTOM_PLUGIN:
{
g_return_val_if_fail (uri, -1);
g_return_val_if_fail (uri, false);
GError * err = NULL;
int ret = apply_plugin (
uri, dest_frames, num_frames, channels, &err);
@ -521,7 +639,7 @@ audio_function_apply ( @@ -521,7 +639,7 @@ audio_function_apply (
{
PROPAGATE_PREFIXED_ERROR (
error, err, "%s", _ ("Failed to apply plugin"));
return ret;
return false;
}
}
break;
@ -572,7 +690,7 @@ audio_function_apply ( @@ -572,7 +690,7 @@ audio_function_apply (
PROPAGATE_PREFIXED_ERROR (
error, err, "%s",
"Failed to write audio clip to pool");
return -1;
return false;
}
audio_sel->pool_id = clip->pool_id;
@ -588,7 +706,7 @@ audio_function_apply ( @@ -588,7 +706,7 @@ audio_function_apply (
PROPAGATE_PREFIXED_ERROR (
error, err, "%s",
"Failed to replace region frames");
return -1;
return false;
}
}
@ -598,6 +716,12 @@ audio_function_apply ( @@ -598,6 +716,12 @@ audio_function_apply (
{
/* set last action */
g_settings_set_int (S_UI, "audio-function", type);
if (type == AUDIO_FUNCTION_PITCH_SHIFT)
{
g_settings_set_double (
S_UI, "audio-function-pitch-shift-ratio",
opts.amount);
}
}
/* free allocated memory */
@ -611,5 +735,5 @@ audio_function_apply ( @@ -611,5 +735,5 @@ audio_function_apply (
EVENTS_PUSH (ET_EDITOR_FUNCTION_APPLIED, NULL);
return 0;
return true;
}

1
src/audio/audio_region.c

@ -215,7 +215,6 @@ audio_region_replace_frames ( @@ -215,7 +215,6 @@ audio_region_replace_frames (
{
g_warn_if_reached ();
/* TODO delete */
int prev_id = clip->pool_id;
GError * err = NULL;
int id = audio_pool_duplicate_clip (

16
src/audio/stretcher.c

@ -36,17 +36,15 @@ @@ -36,17 +36,15 @@
#include <gtk/gtk.h>
/**
* Create a new Stretcher using the rubberband
* backend.
* Create a new Stretcher using the rubberband backend.
*
* @param samplerate The new samplerate.
* @param time_ratio The ratio to multiply time by
* (eg if the BPM is doubled, this will be 0.5).
* @param pitch_ratio The ratio to pitch by. This
* will normally be 1.0 when time-stretching).
* @param realtime Whether to perform realtime
* stretching (lower quality but fast enough to
* be used real-time).
* @param time_ratio The ratio to multiply time by (eg if the
* BPM is doubled, this will be 0.5).
* @param pitch_ratio The ratio to pitch by. This will normally
* be 1.0 when time-stretching).
* @param realtime Whether to perform realtime stretching
* (lower quality but fast enough to be used real-time).
*/
Stretcher *
stretcher_new_rubberband (

11
tests/actions/arranger_selections.c

@ -1668,14 +1668,17 @@ test_audio_functions (void) @@ -1668,14 +1668,17 @@ test_audio_functions (void)
verify_audio_function (orig_frames, frames_per_channel);
/* invert */
AudioFunctionOpts opts = {};
arranger_selections_action_perform_edit_audio_function (
(ArrangerSelections *) AUDIO_SELECTIONS,
AUDIO_FUNCTION_INVERT, NULL, NULL);
AUDIO_FUNCTION_INVERT, opts, NULL, NULL);
verify_audio_function (inverted_frames, frames_per_channel);
test_project_save_and_reload ();
verify_audio_function (inverted_frames, frames_per_channel);
undo_manager_undo (UNDO_MANAGER, NULL);
verify_audio_function (orig_frames, frames_per_channel);
@ -3408,6 +3411,9 @@ main (int argc, char * argv[]) @@ -3408,6 +3411,9 @@ main (int argc, char * argv[])
#define TEST_PREFIX "/actions/arranger_selections/"
g_test_add_func (
TEST_PREFIX "test audio functions",
(GTestFunc) test_audio_functions);
g_test_add_func (
TEST_PREFIX "test delete automation points",
(GTestFunc) test_delete_automation_points);
@ -3417,9 +3423,6 @@ main (int argc, char * argv[]) @@ -3417,9 +3423,6 @@ main (int argc, char * argv[])
g_test_add_func (
TEST_PREFIX "test copy and move automation regions",
(GTestFunc) test_copy_and_move_automation_regions);
g_test_add_func (
TEST_PREFIX "test audio functions",
(GTestFunc) test_audio_functions);
g_test_add_func (
TEST_PREFIX "test move audio_region_and lower samplerate",
(GTestFunc) test_move_audio_region_and_lower_samplerate);

Loading…
Cancel
Save