From 61e05286e204d3bf7ce3d40bfe61ca814074bca8 Mon Sep 17 00:00:00 2001 From: dagargo Date: Fri, 6 Mar 2026 14:24:06 +0100 Subject: [PATCH 01/20] elektron: Add support for Syntakt samples --- res/elektron/devices.json | 3 +- src/connectors/elektron.c | 256 +++++++++++++++++++++++++++++++++++++- src/connectors/elektron.h | 7 +- test/tests_elektron.c | 82 +++++++++++- 4 files changed, 338 insertions(+), 10 deletions(-) diff --git a/res/elektron/devices.json b/res/elektron/devices.json index 731468a9..7122ccef 100644 --- a/res/elektron/devices.json +++ b/res/elektron/devices.json @@ -193,7 +193,8 @@ ], "sound": [ "stsnd" - ] + ], + "data-sample": null }, "storage": [] }, diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 1e17ede2..356d41f4 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -49,6 +49,7 @@ static const gchar *FS_DATA_ANY_EXTS[] = { "data", NULL }; #define FS_DATA_PRJ_PREFIX "/projects" #define FS_DATA_SND_PREFIX "/soundbanks" #define FS_DATA_PST_PREFIX "/presets" +#define FS_DATA_SAMPLES_PREFIX "/samples" #define FS_SMPLRW_START_POS 5 #define FS_DATA_START_POS 18 #define FS_RAM_SLOTS_START_POS 5 @@ -57,6 +58,7 @@ static const gchar *FS_DATA_ANY_EXTS[] = { "data", NULL }; #define FS_SAMPLES_PAD_RES 22 #define ELEKTRON_NAME_MAX_LEN 32 +#define ELEKTRON_DATA_SAMPLE_MAX_LEN 16 #define ELEKTRON_SAMPLE_INFO_PAD_I32_LEN 10 #define ELEKTRON_LOOP_TYPE_FWD 0 @@ -64,6 +66,7 @@ static const gchar *FS_DATA_ANY_EXTS[] = { "data", NULL }; #define PROJECT_SLOTS 128 #define SOUND_SLOTS 256 +#define SAMPLE_SLOTS 64 #define AH_FX_SLOTS 512 #define AH_SLOTS 128 @@ -108,6 +111,43 @@ struct elektron_iterator_data gboolean load_metadata; }; +// These belong to the header but have been removed to avoid alignment issues. +struct elektron_data_header_preamble +{ + guint32 magic_head; // 0xac11d303 + guint8 header_version; +}; + +struct elektron_data_header +{ + guint16 family_id; // 8 for Syntakt + guint16 device_id; // 13 for Syntakt + guint32 os_version; // OS release number + guint32 file_type; // 8 for Syntakt samples + gint32 file_version; + gint32 file_index; + guint32 payload_size; // uncompressed + guint8 is_compressed; + guint8 footer_size; // Size of footer +}; + +struct elektron_data_sample_slot_header +{ + guint32 magic_head; // 0x53414d50 + guint8 version; // Should be 0 + guint8 pad[3]; // ignore + guint32 length; + gchar name[ELEKTRON_DATA_SAMPLE_MAX_LEN]; + guint32 rsvd32[9]; // ignore +}; + +struct elektron_data_footer +{ + guint32 hash; + guint32 content_size; + guint32 magic_tail; // 0xaaa1daaa +}; + typedef GByteArray *(*elektron_msg_id_func) (guint); typedef GByteArray *(*elektron_msg_id_len_func) (guint, guint); @@ -2054,6 +2094,17 @@ elektron_read_data_dir_pst (struct backend *backend, slots); } +static gint +elektron_read_data_dir_sample (struct backend *backend, + struct item_iterator *iter, const gchar *dir, + const gchar **extensions) +{ + return elektron_read_data_dir_prefix (backend, iter, dir, + FS_DATA_SAMPLES_PREFIX, + ITER_MODE_DATA, FS_DATA_START_POS, + SAMPLE_SLOTS); +} + static gint elektron_dst_src_data_prefix_common (struct backend *backend, const gchar *src, const gchar *dst, @@ -2112,6 +2163,13 @@ elektron_move_data_item_pst (struct backend *backend, const gchar *src, FS_DATA_PST_PREFIX); } +static gint +elektron_move_data_item_sample (struct backend *backend, const gchar *src, + const gchar *dst) +{ + return elektron_move_data_item_prefix (backend, src, dst, + FS_DATA_SAMPLES_PREFIX); +} static gint elektron_copy_data_item_prefix (struct backend *backend, const gchar *src, @@ -2153,6 +2211,14 @@ elektron_copy_data_item_pst (struct backend *backend, const gchar *src, FS_DATA_PST_PREFIX); } +static gint +elektron_copy_data_item_sample (struct backend *backend, const gchar *src, + const gchar *dst) +{ + return elektron_copy_data_item_prefix (backend, src, dst, + FS_DATA_SAMPLES_PREFIX); +} + static gint elektron_path_data_prefix_common (struct backend *backend, const gchar *path, const char *prefix, @@ -2200,6 +2266,13 @@ elektron_clear_data_item_pst (struct backend *backend, const gchar *path) return elektron_clear_data_item_prefix (backend, path, FS_DATA_PST_PREFIX); } +static gint +elektron_clear_data_item_sample (struct backend *backend, const gchar *path) +{ + return elektron_clear_data_item_prefix (backend, path, + FS_DATA_SAMPLES_PREFIX); +} + static gint elektron_swap_data_item_prefix (struct backend *backend, const gchar *src, const gchar *dst, const gchar *prefix) @@ -2240,6 +2313,14 @@ elektron_swap_data_item_pst (struct backend *backend, const gchar *src, FS_DATA_PST_PREFIX); } +static gint +elektron_swap_data_item_sample (struct backend *backend, const gchar *src, + const gchar *dst) +{ + return elektron_swap_data_item_prefix (backend, src, dst, + FS_DATA_SAMPLES_PREFIX); +} + static gint elektron_open_datum (struct backend *backend, const gchar *path, guint32 *jid, gint mode, guint32 size) @@ -2274,13 +2355,15 @@ elektron_open_datum (struct backend *backend, const gchar *path, path_cp1252 = elektron_get_cp1252 (path); + // Samples are the only payload type we want to actually work with. + compression = g_str_has_prefix (path, FS_DATA_SAMPLES_PREFIX) ? 0 : 1; + if (mode == O_RDONLY) { g_byte_array_append (tx_msg, (guint8 *) path_cp1252, strlen (path_cp1252) + 1); chunk_size = g_htonl (DATA_TRANSF_BLOCK_BYTES); g_byte_array_append (tx_msg, (guint8 *) & chunk_size, sizeof (guint32)); - compression = 1; g_byte_array_append (tx_msg, &compression, sizeof (guint8)); } @@ -2577,6 +2660,114 @@ elektron_download_data_pst (struct backend *backend, const gchar *path, FS_DATA_PST_PREFIX); } +gint +elektron_set_sample_from_data_sample (struct idata *sample, + struct idata *data_sample) +{ + guint size = data_sample->content->len - + sizeof (struct elektron_data_header_preamble) - + sizeof (struct elektron_data_header) - + sizeof (struct elektron_data_sample_slot_header) - + sizeof (struct elektron_data_footer); + guint sample_data_start = sizeof (struct elektron_data_header_preamble) + + sizeof (struct elektron_data_header) + + sizeof (struct elektron_data_sample_slot_header); + GByteArray *content = g_byte_array_sized_new (size); + + content->len = size; + memcpy (content->data, &data_sample->content->data[sample_data_start], + size); + idata_init (sample, content, elektron_name_to_utf8 (data_sample->name), + NULL, NULL); + + return 0; +} + +gint +elektron_set_data_sample_from_sample (struct idata *data_sample, + struct idata *sample, guint slot) +{ + struct elektron_data_header_preamble preamble; + struct elektron_data_header header; + struct elektron_data_sample_slot_header slot_header; + struct elektron_data_footer footer; + guint size = sample->content->len + + sizeof (struct elektron_data_header_preamble) + + sizeof (struct elektron_data_header) + + sizeof (struct elektron_data_sample_slot_header) + + sizeof (struct elektron_data_footer); + GByteArray *content = g_byte_array_sized_new (size); + + preamble.magic_head = GUINT32_TO_BE (0xac11d303); + preamble.header_version = 2; + + // For now, this is only compatyble with Syntakt. + header.family_id = GUINT16_TO_BE (8); + header.device_id = GUINT16_TO_BE (13); + header.os_version = GUINT32_TO_BE (0x30303832); + header.file_type = GUINT32_TO_BE (8); + header.file_version = 0xffffffff; + header.file_index = GINT32_TO_BE (slot); + header.payload_size = + GUINT32_TO_BE (sample->content->len + + sizeof (struct elektron_data_sample_slot_header)); + header.is_compressed = 0; + header.footer_size = sizeof (struct elektron_data_footer); + + slot_header.magic_head = GUINT32_TO_BE (0x53414d50); + slot_header.version = 0; + slot_header.length = sample->content->len / 2; + snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", + ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, data_sample->name); + + footer.hash = + GUINT32_TO_BE (crc32 + (0xffffffff, sample->content->data, sample->content->len)); + footer.content_size = GUINT32_TO_BE (sample->content->len); + footer.magic_tail = GUINT32_TO_BE (0xaaa1daaa); + + g_byte_array_append (content, (guint8 *) & preamble, + sizeof (struct elektron_data_header_preamble)); + g_byte_array_append (content, (guint8 *) & header, + sizeof (struct elektron_data_header)); + g_byte_array_append (content, (guint8 *) & slot_header, + sizeof (struct elektron_data_sample_slot_header)); + g_byte_array_append (content, sample->content->data, sample->content->len); + g_byte_array_append (content, (guint8 *) & footer, + sizeof (struct elektron_data_footer)); + + idata_init (data_sample, content, elektron_get_cp1252 (sample->name), NULL, + NULL); + return 0; +} + +static gint +elektron_download_data_sample (struct backend *backend, const gchar *path, + struct idata *sample, + struct task_control *control) +{ + gint err; + struct idata data_sample; + + control->parts = 2; + control->part = 0; + + err = elektron_download_data_prefix (backend, path, &data_sample, control, + FS_DATA_SAMPLES_PREFIX); + if (err) + { + return err; + } + + err = elektron_set_sample_from_data_sample (sample, &data_sample); + idata_clear (&data_sample); + + task_control_set_progress (control, 1.0); + control->part++; + + return err; +} + static gchar * elektron_get_download_name (struct backend *backend, const struct fs_operations *ops, @@ -2892,6 +3083,40 @@ elektron_upload_data_pst (struct backend *backend, const gchar *path, FS_DATA_PST_PREFIX); } +static gint +elektron_upload_data_sample (struct backend *backend, const gchar *path, + struct idata *sample, + struct task_control *control) +{ + gint err; + guint id; + struct idata data_sample; + + err = common_slot_get_id_from_path (path, &id); + if (err) + { + return err; + } + + control->parts = 2; + control->part = 0; + + err = elektron_set_data_sample_from_sample (&data_sample, sample, id); + if (err) + { + return err; + } + + task_control_set_progress (control, 1.0); + control->part++; + + err = elektron_upload_data_prefix (backend, path, &data_sample, control, + FS_DATA_SAMPLES_PREFIX); + idata_clear (&data_sample); + + return err; +} + static gint elektron_upload_pkg (struct backend *backend, const gchar *path, struct idata *input, struct task_control *control, @@ -3727,6 +3952,31 @@ static const struct fs_operations FS_DATA_TAKT_II_PST_OPERATIONS = { .get_download_path = elektron_get_download_path }; +static const struct fs_operations FS_DATA_SAMPLES_OPERATIONS = { + .id = FS_DATA_SAMPLES, + .options = FS_OPTION_SLOT_STORAGE | FS_OPTION_SHOW_SIZE_COLUMN | + FS_OPTION_SHOW_SLOT_COLUMN | FS_OPTION_SHOW_INFO_COLUMN | + FS_OPTION_ALLOW_SEARCH, + .name = "data-samples", + .gui_name = "Samples", + .gui_icon = FS_ICON_WAVE, + .file_icon = FS_ICON_WAVE, + .readdir = elektron_read_data_dir_sample, + .print_item = elektron_print_data, + .delete = elektron_clear_data_item_sample, + .move = elektron_move_data_item_sample, + .copy = elektron_copy_data_item_sample, + .swap = elektron_swap_data_item_sample, + .download = elektron_download_data_sample, + .upload = elektron_upload_data_sample, + .get_slot = elektron_get_id_as_slot, + .load = elektron_sample_load, + .save = elektron_sample_save, + .get_exts = sample_get_sample_extensions, + .get_upload_path = common_slot_get_upload_path, + .get_download_path = common_system_get_download_path +}; + static const struct fs_operations FS_DIGITAKT_RAM_OPERATIONS = { .id = FS_DIGITAKT_RAM, .options = FS_OPTION_SAMPLE_EDITOR | FS_OPTION_MONO | @@ -3786,8 +4036,8 @@ static const struct fs_operations *FS_OPERATIONS[] = { &FS_SAMPLES_OPERATIONS, &FS_RAW_ANY_OPERATIONS, &FS_RAW_PRESETS_OPERATIONS, &FS_DATA_ANY_OPERATIONS, &FS_DATA_PRJ_OPERATIONS, &FS_DATA_SND_OPERATIONS, &FS_DATA_PST_OPERATIONS, &FS_DATA_TAKT_II_PST_OPERATIONS, - &FS_DIGITAKT_RAM_OPERATIONS, &FS_DIGITAKT_TRACK_OPERATIONS, - &FS_DIGITAKT_TRACK_LOOP_OPERATIONS, NULL + &FS_DATA_SAMPLES_OPERATIONS, &FS_DIGITAKT_RAM_OPERATIONS, + &FS_DIGITAKT_TRACK_OPERATIONS, &FS_DIGITAKT_TRACK_LOOP_OPERATIONS, NULL }; gint diff --git a/src/connectors/elektron.h b/src/connectors/elektron.h index 0e5417b7..9d7ea0a1 100644 --- a/src/connectors/elektron.h +++ b/src/connectors/elektron.h @@ -43,9 +43,10 @@ enum elektron_fs FS_DATA_SND = (1 << 5), FS_DATA_PST = (1 << 6), FS_DATA_TAKT_II_PST = (1 << 7), - FS_DIGITAKT_RAM = (1 << 8), - FS_DIGITAKT_TRACK = (1 << 9), - FS_DIGITAKT_TRACK_LOOP = (1 << 10) + FS_DATA_SAMPLES = (1 << 8), + FS_DIGITAKT_RAM = (1 << 9), + FS_DIGITAKT_TRACK = (1 << 10), + FS_DIGITAKT_TRACK_LOOP = (1 << 11) }; extern const struct connector CONNECTOR_ELEKTRON; diff --git a/test/tests_elektron.c b/test/tests_elektron.c index a8a59570..1a7b1126 100644 --- a/test/tests_elektron.c +++ b/test/tests_elektron.c @@ -3,6 +3,7 @@ #include #include #include "../config.h" +#include "../src/sample.h" #include "../src/utils.h" #include "../src/connectors/package.h" #include "../src/connectors/elektron.h" @@ -18,7 +19,12 @@ gint elektron_configure_device_from_file (struct backend *backend, guint8 id, void elektron_destroy_data (struct backend *backend); -void +gint elektron_set_sample_from_data_sample (struct idata *sample, + struct idata *data_sample); +gint elektron_set_data_sample_from_sample (struct idata *data_sample, + struct idata *sample, guint slot); + +static void test_elektron_get_dev_exts () { const gchar **exts; @@ -65,7 +71,7 @@ compare_exts (const gchar **a, const gchar **b) return *y != NULL; } -void +static void test_elektron_configure_device_from_file () { const gchar **exts; @@ -120,7 +126,7 @@ test_elektron_configure_device_from_file () CU_ASSERT_EQUAL (backend.data, NULL); } -void +static void test_elektron_special_exts () { const gchar **exts; @@ -185,6 +191,65 @@ test_elektron_special_exts () CU_ASSERT_EQUAL (backend.data, NULL); } +static void +test_elektron_data_sample () +{ + gint err; + struct idata sample, data_sample; + struct task_control task_control; + struct sample_info sample_info_src; + struct sample_load_opts sample_load_opts; + GByteArray *content_before, *content_after; + + controllable_init (&task_control.controllable); + task_control.callback = NULL; + + printf ("\n"); + + sample_load_opts_init (&sample_load_opts, 1, 48000, SF_FORMAT_PCM_16, + FALSE); + err = sample_load_from_file (TEST_DATA_DIR "/connectors/square.wav", + &sample, &task_control, &sample_load_opts, + &sample_info_src); + + controllable_clear (&task_control.controllable); + + CU_ASSERT_EQUAL (err, 0); + if (err) + { + return; + } + + err = elektron_set_data_sample_from_sample (&data_sample, &sample, 3); + CU_ASSERT_EQUAL (err, 0); + if (err) + { + idata_clear (&sample); + return; + } + + content_before = idata_steal (&sample); + + err = elektron_set_sample_from_data_sample (&sample, &data_sample); + CU_ASSERT_EQUAL (err, 0); + if (err) + { + idata_clear (&data_sample); + return; + } + + idata_clear (&data_sample); + + content_after = idata_steal (&sample); + + CU_ASSERT_EQUAL (content_after->len, content_before->len); + CU_ASSERT_EQUAL (memcmp (content_after->data, content_before->data, + content_after->len), 0); + + g_byte_array_free (content_before, TRUE); + g_byte_array_free (content_after, TRUE); +} + gint main (gint argc, gchar *argv[]) { @@ -202,6 +267,12 @@ main (gint argc, gchar *argv[]) goto cleanup; } + if (!CU_add_test (suite, "elektron_get_dev_exts", + test_elektron_get_dev_exts)) + { + goto cleanup; + } + if (!CU_add_test (suite, "elektron_configure_device_from_file", test_elektron_configure_device_from_file)) { @@ -214,6 +285,11 @@ main (gint argc, gchar *argv[]) goto cleanup; } + if (!CU_add_test (suite, "elektron_data_sample", test_elektron_data_sample)) + { + goto cleanup; + } + CU_basic_set_mode (CU_BRM_VERBOSE); CU_basic_run_tests (); From 242473b5ed5e0e689412b9e38b35a8a9bcd9978c Mon Sep 17 00:00:00 2001 From: Gautier Date: Fri, 6 Mar 2026 17:51:56 +0100 Subject: [PATCH 02/20] fix: typo --- src/connectors/elektron.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 356d41f4..fb156422 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -3957,7 +3957,7 @@ static const struct fs_operations FS_DATA_SAMPLES_OPERATIONS = { .options = FS_OPTION_SLOT_STORAGE | FS_OPTION_SHOW_SIZE_COLUMN | FS_OPTION_SHOW_SLOT_COLUMN | FS_OPTION_SHOW_INFO_COLUMN | FS_OPTION_ALLOW_SEARCH, - .name = "data-samples", + .name = "data-sample", .gui_name = "Samples", .gui_icon = FS_ICON_WAVE, .file_icon = FS_ICON_WAVE, From 2c6650500feafc8b2f51d4649b6d04ef3d48e13f Mon Sep 17 00:00:00 2001 From: Gautier Date: Sun, 15 Mar 2026 16:06:43 +0100 Subject: [PATCH 03/20] elektron: data-samples use slot number as name for now --- src/connectors/elektron.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index fb156422..53ac6e2a 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2617,7 +2617,9 @@ elektron_download_data_prefix (struct backend *backend, const gchar *path, if (active) { task_control_set_progress (control, 1.0); - idata_init (data, content, NULL, NULL, NULL); + basename = g_path_get_basename (path); + idata_init (data, content, g_path_get_basename (path), NULL, NULL); + g_free (basename); } else { From eb414032f6a5b46eb3b2d5cf4d5e457941d53af6 Mon Sep 17 00:00:00 2001 From: Gautier Date: Sun, 15 Mar 2026 16:08:31 +0100 Subject: [PATCH 04/20] elektron: fill sample_info to allow save sample to run --- src/connectors/elektron.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 53ac6e2a..e43404fb 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2679,8 +2679,16 @@ elektron_set_sample_from_data_sample (struct idata *sample, content->len = size; memcpy (content->data, &data_sample->content->data[sample_data_start], size); + + struct sample_info *sample_info = NULL; + sample_info = sample_info_new (FALSE); + sample_info->frames = size / 2; + sample_info->rate = 48000; + sample_info->channels = 1; + sample_info->format = SF_FORMAT_PCM_16; + idata_init (sample, content, elektron_name_to_utf8 (data_sample->name), - NULL, NULL); + sample_info, sample_info_free); return 0; } From d2cad43a0d5842d404b341648a71a5f692445c61 Mon Sep 17 00:00:00 2001 From: Gautier Date: Tue, 17 Mar 2026 22:16:54 +0100 Subject: [PATCH 05/20] Add packed attribute to structs --- src/connectors/elektron.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index e43404fb..d0e0cc14 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -112,13 +112,13 @@ struct elektron_iterator_data }; // These belong to the header but have been removed to avoid alignment issues. -struct elektron_data_header_preamble +struct __attribute__((packed)) elektron_data_header_preamble { guint32 magic_head; // 0xac11d303 guint8 header_version; }; -struct elektron_data_header +struct __attribute__((packed)) elektron_data_header { guint16 family_id; // 8 for Syntakt guint16 device_id; // 13 for Syntakt @@ -131,7 +131,7 @@ struct elektron_data_header guint8 footer_size; // Size of footer }; -struct elektron_data_sample_slot_header +struct __attribute__((packed)) elektron_data_sample_slot_header { guint32 magic_head; // 0x53414d50 guint8 version; // Should be 0 @@ -141,7 +141,7 @@ struct elektron_data_sample_slot_header guint32 rsvd32[9]; // ignore }; -struct elektron_data_footer +struct __attribute__((packed)) elektron_data_footer { guint32 hash; guint32 content_size; From 45dcf2109ce4b5075fca62c6c1348cf0a2d261ac Mon Sep 17 00:00:00 2001 From: Gautier Date: Tue, 17 Mar 2026 22:43:08 +0100 Subject: [PATCH 06/20] Read sample name in header --- src/connectors/elektron.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index d0e0cc14..d4e337c7 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2617,9 +2617,7 @@ elektron_download_data_prefix (struct backend *backend, const gchar *path, if (active) { task_control_set_progress (control, 1.0); - basename = g_path_get_basename (path); - idata_init (data, content, g_path_get_basename (path), NULL, NULL); - g_free (basename); + idata_init (data, content, NULL, NULL, NULL); } else { @@ -2674,6 +2672,21 @@ elektron_set_sample_from_data_sample (struct idata *sample, guint sample_data_start = sizeof (struct elektron_data_header_preamble) + sizeof (struct elektron_data_header) + sizeof (struct elektron_data_sample_slot_header); + struct elektron_data_sample_slot_header *slot_header = + (struct elektron_data_sample_slot_header *) &data_sample->content->data + [sizeof (struct elektron_data_header_preamble) + + sizeof (struct elektron_data_header)]; + gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN]; + gint j = 0; + for (gint i = 0; i < ELEKTRON_DATA_SAMPLE_MAX_LEN - 1 && slot_header->name[i] != '\0'; i++) + { + if (g_ascii_isprint (slot_header->name[i]) && slot_header->name[i] != '/' && + slot_header->name[i] != '\\') + { + safe_name[j++] = slot_header->name[i]; + } + } + safe_name[j] = '\0'; GByteArray *content = g_byte_array_sized_new (size); content->len = size; @@ -2687,7 +2700,7 @@ elektron_set_sample_from_data_sample (struct idata *sample, sample_info->channels = 1; sample_info->format = SF_FORMAT_PCM_16; - idata_init (sample, content, elektron_name_to_utf8 (data_sample->name), + idata_init (sample, content, elektron_name_to_utf8 (safe_name), sample_info, sample_info_free); return 0; From 8d4541e28babf8558fa879e8010050378ddf5e4b Mon Sep 17 00:00:00 2001 From: Gautier Date: Tue, 17 Mar 2026 23:04:38 +0100 Subject: [PATCH 07/20] Convert sample data to/from BE --- src/connectors/elektron.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index d4e337c7..e11a7c04 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2676,6 +2676,9 @@ elektron_set_sample_from_data_sample (struct idata *sample, (struct elektron_data_sample_slot_header *) &data_sample->content->data [sizeof (struct elektron_data_header_preamble) + sizeof (struct elektron_data_header)]; + struct elektron_data_footer *footer = + (struct elektron_data_footer *) &data_sample->content->data + [data_sample->content->len - sizeof (struct elektron_data_footer)]; gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN]; gint j = 0; for (gint i = 0; i < ELEKTRON_DATA_SAMPLE_MAX_LEN - 1 && slot_header->name[i] != '\0'; i++) @@ -2688,10 +2691,16 @@ elektron_set_sample_from_data_sample (struct idata *sample, } safe_name[j] = '\0'; GByteArray *content = g_byte_array_sized_new (size); + g_byte_array_set_size (content, size); - content->len = size; - memcpy (content->data, &data_sample->content->data[sample_data_start], - size); + const gint16 *src = (const gint16 *) &data_sample->content->data[sample_data_start]; + gint16 *dest = (gint16 *) content->data; + guint samples_count = size / 2; + for (guint i = 0; i < samples_count; i++) + { + dest[i] = GINT16_FROM_BE (src[i]); + } + } struct sample_info *sample_info = NULL; sample_info = sample_info_new (FALSE); @@ -2755,7 +2764,14 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, sizeof (struct elektron_data_header)); g_byte_array_append (content, (guint8 *) & slot_header, sizeof (struct elektron_data_sample_slot_header)); - g_byte_array_append (content, sample->content->data, sample->content->len); + g_byte_array_set_size (content, content->len + sample->content->len); + const gint16 *src = (const gint16 *) sample->content->data; + guint samples_count = sample->content->len / 2; + gint16 *dest = (gint16 *) &content->data[content->len - sample->content->len]; + for (guint i = 0; i < samples_count; i++) + { + dest[i] = GINT16_TO_BE (src[i]); + } g_byte_array_append (content, (guint8 *) & footer, sizeof (struct elektron_data_footer)); From c44c592d666a8d3bfceb0cce969b1fb6c74741d4 Mon Sep 17 00:00:00 2001 From: Gautier Date: Tue, 17 Mar 2026 23:05:28 +0100 Subject: [PATCH 08/20] Length must be BE --- src/connectors/elektron.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index e11a7c04..a07d56d5 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2748,7 +2748,7 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, slot_header.magic_head = GUINT32_TO_BE (0x53414d50); slot_header.version = 0; - slot_header.length = sample->content->len / 2; + slot_header.length = GINT32_TO_BE (sample->content->len / 2); snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, data_sample->name); From 95a47438fb98a4e89648f84ec2b413fa7e9614db Mon Sep 17 00:00:00 2001 From: Gautier Date: Tue, 17 Mar 2026 23:06:47 +0100 Subject: [PATCH 09/20] Check CRC of downloaded samples --- src/connectors/elektron.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index a07d56d5..910e9f74 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2700,6 +2700,21 @@ elektron_set_sample_from_data_sample (struct idata *sample, { dest[i] = GINT16_FROM_BE (src[i]); } + + guint crc_start = sizeof (struct elektron_data_header_preamble) + + sizeof (struct elektron_data_header); + guint32 crc = crc32 (0xffffffff, + &data_sample->content->data[crc_start], + sizeof (struct elektron_data_sample_slot_header)); + crc = crc32 (crc, &data_sample->content->data[sample_data_start], size); + debug_print (1, "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x\n", + size, crc); + + if (crc != GUINT32_FROM_BE (footer->hash)) + { + error_print ("CRC mismatch: calculated 0x%08x, expected 0x%08x", + crc, GUINT32_FROM_BE (footer->hash)); + return -1; } struct sample_info *sample_info = NULL; From 8a0539427e5caed2a7665232d72c42cc90118619 Mon Sep 17 00:00:00 2001 From: Gautier Date: Tue, 17 Mar 2026 23:15:07 +0100 Subject: [PATCH 10/20] Calculate CRC after BE conversion, CRC includes slot-header --- src/connectors/elektron.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 910e9f74..118dacd7 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2767,11 +2767,6 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, data_sample->name); - footer.hash = - GUINT32_TO_BE (crc32 - (0xffffffff, sample->content->data, sample->content->len)); - footer.content_size = GUINT32_TO_BE (sample->content->len); - footer.magic_tail = GUINT32_TO_BE (0xaaa1daaa); g_byte_array_append (content, (guint8 *) & preamble, sizeof (struct elektron_data_header_preamble)); @@ -2787,6 +2782,13 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, { dest[i] = GINT16_TO_BE (src[i]); } + guint32 crc = crc32 (0xffffffff, (guint8 *) & slot_header, + sizeof (struct elektron_data_sample_slot_header)); + crc = crc32 (crc, (guint8 *) dest, sample->content->len); + footer.hash = GUINT32_TO_BE (crc); + footer.content_size = GUINT32_TO_BE (sample->content->len + + sizeof (struct elektron_data_sample_slot_header)); + footer.magic_tail = GUINT32_TO_BE (0xaaa1daaa); g_byte_array_append (content, (guint8 *) & footer, sizeof (struct elektron_data_footer)); From 1a611c6768545461bb1e79c3016753fd4e98b5fb Mon Sep 17 00:00:00 2001 From: Gautier Date: Tue, 17 Mar 2026 23:17:31 +0100 Subject: [PATCH 11/20] Use basename as sample name --- src/connectors/elektron.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 118dacd7..d42ce6d0 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2764,9 +2764,11 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, slot_header.magic_head = GUINT32_TO_BE (0x53414d50); slot_header.version = 0; slot_header.length = GINT32_TO_BE (sample->content->len / 2); + + gchar * basename = elektron_get_cp1252 (g_path_get_basename (sample->name)); snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", - ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, data_sample->name); - + ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, basename); + g_free (basename); g_byte_array_append (content, (guint8 *) & preamble, sizeof (struct elektron_data_header_preamble)); @@ -2792,7 +2794,7 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, g_byte_array_append (content, (guint8 *) & footer, sizeof (struct elektron_data_footer)); - idata_init (data_sample, content, elektron_get_cp1252 (sample->name), NULL, + idata_init (data_sample, content, basename, NULL, NULL); return 0; } From f7ea2e8814cbb0e2dbc3fc607f63007d3b33a3a2 Mon Sep 17 00:00:00 2001 From: Gautier Date: Wed, 18 Mar 2026 18:31:25 +0100 Subject: [PATCH 12/20] Fix progress when downloading sample --- src/connectors/elektron.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index d42ce6d0..efe25bc1 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2820,8 +2820,8 @@ elektron_download_data_sample (struct backend *backend, const gchar *path, err = elektron_set_sample_from_data_sample (sample, &data_sample); idata_clear (&data_sample); - task_control_set_progress (control, 1.0); control->part++; + task_control_set_progress (control, 1.0); return err; } From 79d149ad42040529d930335c3953656cfca1b18b Mon Sep 17 00:00:00 2001 From: Gautier Date: Wed, 18 Mar 2026 18:35:55 +0100 Subject: [PATCH 13/20] Merge elektron_data_header_preamble into elektron_data_header --- src/connectors/elektron.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index efe25bc1..71a1d453 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -111,15 +111,10 @@ struct elektron_iterator_data gboolean load_metadata; }; -// These belong to the header but have been removed to avoid alignment issues. -struct __attribute__((packed)) elektron_data_header_preamble +struct __attribute__((packed)) elektron_data_header { guint32 magic_head; // 0xac11d303 guint8 header_version; -}; - -struct __attribute__((packed)) elektron_data_header -{ guint16 family_id; // 8 for Syntakt guint16 device_id; // 13 for Syntakt guint32 os_version; // OS release number @@ -2665,17 +2660,14 @@ elektron_set_sample_from_data_sample (struct idata *sample, struct idata *data_sample) { guint size = data_sample->content->len - - sizeof (struct elektron_data_header_preamble) - sizeof (struct elektron_data_header) - sizeof (struct elektron_data_sample_slot_header) - sizeof (struct elektron_data_footer); - guint sample_data_start = sizeof (struct elektron_data_header_preamble) + - sizeof (struct elektron_data_header) + + guint sample_data_start = sizeof (struct elektron_data_header) + sizeof (struct elektron_data_sample_slot_header); struct elektron_data_sample_slot_header *slot_header = (struct elektron_data_sample_slot_header *) &data_sample->content->data - [sizeof (struct elektron_data_header_preamble) + - sizeof (struct elektron_data_header)]; + [sizeof (struct elektron_data_header)]; struct elektron_data_footer *footer = (struct elektron_data_footer *) &data_sample->content->data [data_sample->content->len - sizeof (struct elektron_data_footer)]; @@ -2701,8 +2693,7 @@ elektron_set_sample_from_data_sample (struct idata *sample, dest[i] = GINT16_FROM_BE (src[i]); } - guint crc_start = sizeof (struct elektron_data_header_preamble) + - sizeof (struct elektron_data_header); + guint crc_start = sizeof (struct elektron_data_header); guint32 crc = crc32 (0xffffffff, &data_sample->content->data[crc_start], sizeof (struct elektron_data_sample_slot_header)); @@ -2734,19 +2725,17 @@ gint elektron_set_data_sample_from_sample (struct idata *data_sample, struct idata *sample, guint slot) { - struct elektron_data_header_preamble preamble; struct elektron_data_header header; struct elektron_data_sample_slot_header slot_header; struct elektron_data_footer footer; guint size = sample->content->len + - sizeof (struct elektron_data_header_preamble) + sizeof (struct elektron_data_header) + sizeof (struct elektron_data_sample_slot_header) + sizeof (struct elektron_data_footer); GByteArray *content = g_byte_array_sized_new (size); - preamble.magic_head = GUINT32_TO_BE (0xac11d303); - preamble.header_version = 2; + header.magic_head = GUINT32_TO_BE (0xac11d303); + header.header_version = 2; // For now, this is only compatyble with Syntakt. header.family_id = GUINT16_TO_BE (8); From dcfc158ccefddaea07719d4081c5440745e301f5 Mon Sep 17 00:00:00 2001 From: Gautier Date: Wed, 18 Mar 2026 18:36:32 +0100 Subject: [PATCH 14/20] Fix formatting --- src/connectors/elektron.c | 42 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 71a1d453..78e64a0c 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2673,10 +2673,12 @@ elektron_set_sample_from_data_sample (struct idata *sample, [data_sample->content->len - sizeof (struct elektron_data_footer)]; gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN]; gint j = 0; - for (gint i = 0; i < ELEKTRON_DATA_SAMPLE_MAX_LEN - 1 && slot_header->name[i] != '\0'; i++) + for (gint i = 0; + i < ELEKTRON_DATA_SAMPLE_MAX_LEN - 1 && slot_header->name[i] != '\0'; + i++) { - if (g_ascii_isprint (slot_header->name[i]) && slot_header->name[i] != '/' && - slot_header->name[i] != '\\') + if (g_ascii_isprint (slot_header->name[i]) + && slot_header->name[i] != '/' && slot_header->name[i] != '\\') { safe_name[j++] = slot_header->name[i]; } @@ -2685,7 +2687,8 @@ elektron_set_sample_from_data_sample (struct idata *sample, GByteArray *content = g_byte_array_sized_new (size); g_byte_array_set_size (content, size); - const gint16 *src = (const gint16 *) &data_sample->content->data[sample_data_start]; + const gint16 *src = + (const gint16 *) &data_sample->content->data[sample_data_start]; gint16 *dest = (gint16 *) content->data; guint samples_count = size / 2; for (guint i = 0; i < samples_count; i++) @@ -2698,13 +2701,14 @@ elektron_set_sample_from_data_sample (struct idata *sample, &data_sample->content->data[crc_start], sizeof (struct elektron_data_sample_slot_header)); crc = crc32 (crc, &data_sample->content->data[sample_data_start], size); - debug_print (1, "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x\n", - size, crc); + debug_print (1, + "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x\n", + size, crc); if (crc != GUINT32_FROM_BE (footer->hash)) { error_print ("CRC mismatch: calculated 0x%08x, expected 0x%08x", - crc, GUINT32_FROM_BE (footer->hash)); + crc, GUINT32_FROM_BE (footer->hash)); return -1; } @@ -2737,7 +2741,7 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, header.magic_head = GUINT32_TO_BE (0xac11d303); header.header_version = 2; - // For now, this is only compatyble with Syntakt. + // For now, this is only compatible with Syntakt. header.family_id = GUINT16_TO_BE (8); header.device_id = GUINT16_TO_BE (13); header.os_version = GUINT32_TO_BE (0x30303832); @@ -2753,38 +2757,42 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, slot_header.magic_head = GUINT32_TO_BE (0x53414d50); slot_header.version = 0; slot_header.length = GINT32_TO_BE (sample->content->len / 2); - - gchar * basename = elektron_get_cp1252 (g_path_get_basename (sample->name)); + + gchar *basename = elektron_get_cp1252 (g_path_get_basename (sample->name)); snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, basename); - g_free (basename); + g_free (basename); - g_byte_array_append (content, (guint8 *) & preamble, - sizeof (struct elektron_data_header_preamble)); g_byte_array_append (content, (guint8 *) & header, sizeof (struct elektron_data_header)); g_byte_array_append (content, (guint8 *) & slot_header, sizeof (struct elektron_data_sample_slot_header)); g_byte_array_set_size (content, content->len + sample->content->len); + + /* Convert sample data from host to big-endian byte order for CRC and storage */ const gint16 *src = (const gint16 *) sample->content->data; guint samples_count = sample->content->len / 2; - gint16 *dest = (gint16 *) &content->data[content->len - sample->content->len]; + gint16 *dest = + (gint16 *) & content->data[content->len - sample->content->len]; for (guint i = 0; i < samples_count; i++) { dest[i] = GINT16_TO_BE (src[i]); } + + /* CRC includes slot_header + big-endian sample data */ guint32 crc = crc32 (0xffffffff, (guint8 *) & slot_header, sizeof (struct elektron_data_sample_slot_header)); crc = crc32 (crc, (guint8 *) dest, sample->content->len); footer.hash = GUINT32_TO_BE (crc); footer.content_size = GUINT32_TO_BE (sample->content->len + - sizeof (struct elektron_data_sample_slot_header)); + sizeof (struct + elektron_data_sample_slot_header)); footer.magic_tail = GUINT32_TO_BE (0xaaa1daaa); + g_byte_array_append (content, (guint8 *) & footer, sizeof (struct elektron_data_footer)); - idata_init (data_sample, content, basename, NULL, - NULL); + idata_init (data_sample, content, basename, NULL, NULL); return 0; } From aa1bdc1f6fde182cdbcd20cf461957e9032dc081 Mon Sep 17 00:00:00 2001 From: Gautier Date: Wed, 18 Mar 2026 20:46:43 +0100 Subject: [PATCH 15/20] Simplify sample name handling --- src/connectors/elektron.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 78e64a0c..ccc44f82 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2672,18 +2672,8 @@ elektron_set_sample_from_data_sample (struct idata *sample, (struct elektron_data_footer *) &data_sample->content->data [data_sample->content->len - sizeof (struct elektron_data_footer)]; gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN]; - gint j = 0; - for (gint i = 0; - i < ELEKTRON_DATA_SAMPLE_MAX_LEN - 1 && slot_header->name[i] != '\0'; - i++) - { - if (g_ascii_isprint (slot_header->name[i]) - && slot_header->name[i] != '/' && slot_header->name[i] != '\\') - { - safe_name[j++] = slot_header->name[i]; - } - } - safe_name[j] = '\0'; + snprintf (safe_name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", + ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, slot_header->name); GByteArray *content = g_byte_array_sized_new (size); g_byte_array_set_size (content, size); @@ -2758,10 +2748,9 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, slot_header.version = 0; slot_header.length = GINT32_TO_BE (sample->content->len / 2); - gchar *basename = elektron_get_cp1252 (g_path_get_basename (sample->name)); + gchar *name = elektron_get_cp1252 (sample->name); snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", - ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, basename); - g_free (basename); + ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, name); g_byte_array_append (content, (guint8 *) & header, sizeof (struct elektron_data_header)); @@ -2792,7 +2781,7 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, g_byte_array_append (content, (guint8 *) & footer, sizeof (struct elektron_data_footer)); - idata_init (data_sample, content, basename, NULL, NULL); + idata_init (data_sample, content, NULL, NULL, NULL); return 0; } From 7b77053de946a4a3ccf8be2c69d6469e397ecc5b Mon Sep 17 00:00:00 2001 From: dagargo Date: Thu, 19 Mar 2026 12:55:58 +0100 Subject: [PATCH 16/20] elektron: Fix memory leak --- src/connectors/elektron.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index ccc44f82..fb98fc50 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2751,6 +2751,7 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, gchar *name = elektron_get_cp1252 (sample->name); snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, name); + g_free (name); g_byte_array_append (content, (guint8 *) & header, sizeof (struct elektron_data_header)); From b1c5af319d2179fe162f022bd1a72567734b63f1 Mon Sep 17 00:00:00 2001 From: dagargo Date: Thu, 19 Mar 2026 13:28:04 +0100 Subject: [PATCH 17/20] elektron: Rename variable --- src/connectors/elektron.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index fb98fc50..f01e64ae 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2987,7 +2987,7 @@ elektron_upload_data_prefix (struct backend *backend, const gchar *path, GByteArray *rx_msg; GByteArray *tx_msg; gchar *path_w_prefix; - GByteArray *array = data->content; + GByteArray *content = data->content; err = common_slot_get_id_from_path (path, &id); if (err) @@ -2997,7 +2997,7 @@ elektron_upload_data_prefix (struct backend *backend, const gchar *path, path_w_prefix = elektron_add_prefix_to_path (path, prefix); err = elektron_open_datum (backend, path_w_prefix, &jid, O_WRONLY, - array->len); + content->len); g_free (path_w_prefix); if (err) { @@ -3013,7 +3013,7 @@ elektron_upload_data_prefix (struct backend *backend, const gchar *path, active = controllable_is_active (&control->controllable); - while (offset < array->len && active) + while (offset < content->len && active) { tx_msg = elektron_new_msg (DATA_WRITE_PARTIAL_REQUEST, sizeof (DATA_WRITE_PARTIAL_REQUEST)); @@ -3021,23 +3021,23 @@ elektron_upload_data_prefix (struct backend *backend, const gchar *path, aux32 = g_htonl (seq); g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); - if (offset + DATA_TRANSF_BLOCK_BYTES < array->len) + if (offset + DATA_TRANSF_BLOCK_BYTES < content->len) { len = DATA_TRANSF_BLOCK_BYTES; } else { - len = array->len - offset; + len = content->len - offset; } - crc = crc32 (0xffffffff, &array->data[offset], len); + crc = crc32 (0xffffffff, &content->data[offset], len); aux32 = g_htonl (crc); g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); aux32 = g_htonl (len); g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); - g_byte_array_append (tx_msg, &array->data[offset], len); + g_byte_array_append (tx_msg, &content->data[offset], len); rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable); if (!rx_msg) @@ -3082,14 +3082,14 @@ elektron_upload_data_prefix (struct backend *backend, const gchar *path, total, offset); } - task_control_set_progress (control, offset / (gdouble) array->len); + task_control_set_progress (control, offset / (gdouble) content->len); active = controllable_is_active (&control->controllable); } debug_print (2, "%d bytes sent", offset); - err = elektron_close_datum (backend, jid, O_WRONLY, array->len); + err = elektron_close_datum (backend, jid, O_WRONLY, content->len); end: return err; From b83c80ff4da4899c52539ab66f28a4c923d300e0 Mon Sep 17 00:00:00 2001 From: dagargo Date: Thu, 19 Mar 2026 21:07:01 +0100 Subject: [PATCH 18/20] elektron: Refactor code to allow multiple partial writing requests --- src/connectors/elektron.c | 299 ++++++++++++++++++++++---------------- test/tests_elektron.c | 10 +- 2 files changed, 182 insertions(+), 127 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index f01e64ae..49b768f3 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2655,6 +2655,8 @@ elektron_download_data_pst (struct backend *backend, const gchar *path, FS_DATA_PST_PREFIX); } +// In this case, data_sample contains the footer too. + gint elektron_set_sample_from_data_sample (struct idata *sample, struct idata *data_sample) @@ -2671,29 +2673,15 @@ elektron_set_sample_from_data_sample (struct idata *sample, struct elektron_data_footer *footer = (struct elektron_data_footer *) &data_sample->content->data [data_sample->content->len - sizeof (struct elektron_data_footer)]; - gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN]; - snprintf (safe_name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", - ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, slot_header->name); - GByteArray *content = g_byte_array_sized_new (size); - g_byte_array_set_size (content, size); - - const gint16 *src = - (const gint16 *) &data_sample->content->data[sample_data_start]; - gint16 *dest = (gint16 *) content->data; - guint samples_count = size / 2; - for (guint i = 0; i < samples_count; i++) - { - dest[i] = GINT16_FROM_BE (src[i]); - } guint crc_start = sizeof (struct elektron_data_header); + guint crc_size = sizeof (struct elektron_data_sample_slot_header) + size; guint32 crc = crc32 (0xffffffff, &data_sample->content->data[crc_start], - sizeof (struct elektron_data_sample_slot_header)); - crc = crc32 (crc, &data_sample->content->data[sample_data_start], size); + crc_size); debug_print (1, - "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x\n", - size, crc); + "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x", + crc_size, crc); if (crc != GUINT32_FROM_BE (footer->hash)) { @@ -2702,9 +2690,24 @@ elektron_set_sample_from_data_sample (struct idata *sample, return -1; } - struct sample_info *sample_info = NULL; + gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN]; + snprintf (safe_name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", + ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, slot_header->name); + GByteArray *content = g_byte_array_sized_new (size); + content->len = size; + + const gint16 *src = + (const gint16 *) &data_sample->content->data[sample_data_start]; + gint16 *dest = (gint16 *) content->data; + guint frames = size / 2; + for (guint i = 0; i < frames; i++) + { + dest[i] = GINT16_FROM_BE (src[i]); + } + + struct sample_info *sample_info; sample_info = sample_info_new (FALSE); - sample_info->frames = size / 2; + sample_info->frames = frames; sample_info->rate = 48000; sample_info->channels = 1; sample_info->format = SF_FORMAT_PCM_16; @@ -2715,18 +2718,26 @@ elektron_set_sample_from_data_sample (struct idata *sample, return 0; } +// Due to a firmware limitation when working with uncompressed data, +// footer must be transferred in a different partial request. + gint elektron_set_data_sample_from_sample (struct idata *data_sample, + struct idata *data_footer, struct idata *sample, guint slot) { + guint size; struct elektron_data_header header; struct elektron_data_sample_slot_header slot_header; struct elektron_data_footer footer; - guint size = sample->content->len + - sizeof (struct elektron_data_header) + - sizeof (struct elektron_data_sample_slot_header) + - sizeof (struct elektron_data_footer); - GByteArray *content = g_byte_array_sized_new (size); + GByteArray *sample_content, *footer_content; + + size = sizeof (struct elektron_data_header) + + sizeof (struct elektron_data_sample_slot_header) + sample->content->len; + sample_content = g_byte_array_sized_new (size); + + size = sizeof (struct elektron_data_footer); + footer_content = g_byte_array_sized_new (size); header.magic_head = GUINT32_TO_BE (0xac11d303); header.header_version = 2; @@ -2739,50 +2750,55 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, header.file_version = 0xffffffff; header.file_index = GINT32_TO_BE (slot); header.payload_size = - GUINT32_TO_BE (sample->content->len + - sizeof (struct elektron_data_sample_slot_header)); + GUINT32_TO_BE (sizeof (struct elektron_data_sample_slot_header) + + sample->content->len); header.is_compressed = 0; header.footer_size = sizeof (struct elektron_data_footer); + guint frames = sample->content->len / 2; slot_header.magic_head = GUINT32_TO_BE (0x53414d50); slot_header.version = 0; - slot_header.length = GINT32_TO_BE (sample->content->len / 2); + slot_header.length = GINT32_TO_BE (frames); gchar *name = elektron_get_cp1252 (sample->name); snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, name); g_free (name); - g_byte_array_append (content, (guint8 *) & header, + g_byte_array_append (sample_content, (guint8 *) & header, sizeof (struct elektron_data_header)); - g_byte_array_append (content, (guint8 *) & slot_header, + g_byte_array_append (sample_content, (guint8 *) & slot_header, sizeof (struct elektron_data_sample_slot_header)); - g_byte_array_set_size (content, content->len + sample->content->len); /* Convert sample data from host to big-endian byte order for CRC and storage */ const gint16 *src = (const gint16 *) sample->content->data; - guint samples_count = sample->content->len / 2; - gint16 *dest = - (gint16 *) & content->data[content->len - sample->content->len]; - for (guint i = 0; i < samples_count; i++) + gint16 *dest = (gint16 *) & sample_content->data[sample_content->len]; + for (guint i = 0; i < frames; i++) { dest[i] = GINT16_TO_BE (src[i]); } + sample_content->len += sample->content->len; /* CRC includes slot_header + big-endian sample data */ - guint32 crc = crc32 (0xffffffff, (guint8 *) & slot_header, - sizeof (struct elektron_data_sample_slot_header)); - crc = crc32 (crc, (guint8 *) dest, sample->content->len); + guint crc_start = sizeof (struct elektron_data_header); + guint crc_size = sizeof (struct elektron_data_sample_slot_header) + + sample->content->len; + guint32 crc = crc32 (0xffffffff, (guint8 *) & sample_content->data[crc_start], + crc_size); + debug_print (1, + "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x", + crc_size, crc); + footer.hash = GUINT32_TO_BE (crc); - footer.content_size = GUINT32_TO_BE (sample->content->len + - sizeof (struct - elektron_data_sample_slot_header)); + footer.content_size = GUINT32_TO_BE (size); footer.magic_tail = GUINT32_TO_BE (0xaaa1daaa); - g_byte_array_append (content, (guint8 *) & footer, + g_byte_array_append (footer_content, (guint8 *) & footer, sizeof (struct elektron_data_footer)); - idata_init (data_sample, content, NULL, NULL, NULL); + idata_init (data_sample, sample_content, NULL, NULL, NULL); + idata_init (data_footer, footer_content, NULL, NULL, NULL); + return 0; } @@ -2965,29 +2981,19 @@ elektron_get_download_path (struct backend *backend, } static gint -elektron_upload_data_prefix (struct backend *backend, const gchar *path, - struct idata *data, - struct task_control *control, - const gchar *prefix) +elektron_upload_data_list_prefix (struct backend *backend, const gchar *path, + GSList *list, struct task_control *control, + const gchar *prefix) { gint err; guint id; - guint32 seq; - guint32 jid; - guint32 crc; - guint32 len; - guint32 r_jid; - guint32 r_seq; - guint32 offset; - guint32 *data32; - guint32 jidbe; - guint32 aux32; + GSList *iter; gboolean active; - guint32 total; - GByteArray *rx_msg; - GByteArray *tx_msg; + struct idata *data; gchar *path_w_prefix; - GByteArray *content = data->content; + GByteArray *rx_msg, *tx_msg, *content; + guint32 seq, jid, jidbe, crc, len, r_jid, r_seq, offset, *data32, + aux32, total, full_content_size, full_content_sent; err = common_slot_get_id_from_path (path, &id); if (err) @@ -2995,9 +3001,17 @@ elektron_upload_data_prefix (struct backend *backend, const gchar *path, return err; } + full_content_size = 0; + for (iter = list; iter != NULL; iter = iter->next) + { + data = iter->data; + content = data->content; + full_content_size += content->len; + } + path_w_prefix = elektron_add_prefix_to_path (path, prefix); err = elektron_open_datum (backend, path_w_prefix, &jid, O_WRONLY, - content->len); + full_content_size); g_free (path_w_prefix); if (err) { @@ -3009,92 +3023,123 @@ elektron_upload_data_prefix (struct backend *backend, const gchar *path, jidbe = g_htonl (jid); seq = 0; - offset = 0; + full_content_sent = 0; + for (iter = list; iter != NULL; iter = iter->next) + { + data = iter->data; + content = data->content; - active = controllable_is_active (&control->controllable); + offset = 0; - while (offset < content->len && active) - { - tx_msg = elektron_new_msg (DATA_WRITE_PARTIAL_REQUEST, - sizeof (DATA_WRITE_PARTIAL_REQUEST)); - g_byte_array_append (tx_msg, (guint8 *) & jidbe, sizeof (guint32)); - aux32 = g_htonl (seq); - g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); + active = controllable_is_active (&control->controllable); - if (offset + DATA_TRANSF_BLOCK_BYTES < content->len) + while (offset < content->len && active) { - len = DATA_TRANSF_BLOCK_BYTES; - } - else - { - len = content->len - offset; - } + tx_msg = elektron_new_msg (DATA_WRITE_PARTIAL_REQUEST, + sizeof (DATA_WRITE_PARTIAL_REQUEST)); + g_byte_array_append (tx_msg, (guint8 *) & jidbe, sizeof (guint32)); + aux32 = g_htonl (seq); + g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); - crc = crc32 (0xffffffff, &content->data[offset], len); - aux32 = g_htonl (crc); - g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); + if (offset + DATA_TRANSF_BLOCK_BYTES < content->len) + { + len = DATA_TRANSF_BLOCK_BYTES; + } + else + { + len = content->len - offset; + } - aux32 = g_htonl (len); - g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); + crc = crc32 (0xffffffff, &content->data[offset], len); + aux32 = g_htonl (crc); + g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); - g_byte_array_append (tx_msg, &content->data[offset], len); + aux32 = g_htonl (len); + g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); - rx_msg = elektron_tx_and_rx (backend, tx_msg, &control->controllable); - if (!rx_msg) - { - err = -EIO; - goto end; - } + g_byte_array_append (tx_msg, &content->data[offset], len); - usleep (BE_REST_TIME_US); + rx_msg = elektron_tx_and_rx (backend, tx_msg, + &control->controllable); + if (!rx_msg) + { + err = -EIO; + goto end; + } - if (!elektron_get_msg_status (rx_msg)) - { - err = -EPERM; - error_print ("%s (%s)", backend_strerror (backend, err), - elektron_get_msg_string (rx_msg)); - free_msg (rx_msg); - break; - } + usleep (BE_REST_TIME_US); - data32 = (guint32 *) & rx_msg->data[6]; - r_jid = g_ntohl (*data32); + if (!elektron_get_msg_status (rx_msg)) + { + err = -EPERM; + error_print ("%s (%s)", backend_strerror (backend, err), + elektron_get_msg_string (rx_msg)); + free_msg (rx_msg); + break; + } - data32 = (guint32 *) & rx_msg->data[10]; - r_seq = g_ntohl (*data32); + data32 = (guint32 *) & rx_msg->data[6]; + r_jid = g_ntohl (*data32); - data32 = (guint32 *) & rx_msg->data[14]; - total = g_ntohl (*data32); + data32 = (guint32 *) & rx_msg->data[10]; + r_seq = g_ntohl (*data32); - free_msg (rx_msg); + data32 = (guint32 *) & rx_msg->data[14]; + total = g_ntohl (*data32); - debug_print (1, - "Write datum info: job id: %d; seq: %d; total: %d", - r_jid, r_seq, total); + free_msg (rx_msg); - seq++; - offset += len; + debug_print (1, + "Write datum info: job id: %d; seq: %d; total: %d", + r_jid, r_seq, total); - if (total != offset) - { - error_print - ("Actual upload bytes (%d) differs from expected ones (%d)", - total, offset); + seq++; + offset += len; + + if (total != offset) + { + error_print + ("Actual upload bytes (%d) differs from expected ones (%d)", + total, offset); + } + + gdouble progress = (full_content_sent + offset) / + (gdouble) full_content_size; + task_control_set_progress (control, progress); + + active = controllable_is_active (&control->controllable); } - task_control_set_progress (control, offset / (gdouble) content->len); + debug_print (2, "Partial request with %d bytes sent", offset); - active = controllable_is_active (&control->controllable); + full_content_sent += content->len; } - debug_print (2, "%d bytes sent", offset); + debug_print (2, "%d bytes sent", full_content_sent); - err = elektron_close_datum (backend, jid, O_WRONLY, content->len); + err = elektron_close_datum (backend, jid, O_WRONLY, full_content_size); end: return err; } +static gint +elektron_upload_data_prefix (struct backend *backend, const gchar *path, + struct idata *data, + struct task_control *control, + const gchar *prefix) +{ + gint err; + GSList *list; + + list = g_slist_append (NULL, data); + err = elektron_upload_data_list_prefix (backend, path, list, control, + prefix); + g_slist_free (list); + + return err; +} + static gint elektron_upload_data_any (struct backend *backend, const gchar *path, struct idata *any, struct task_control *control) @@ -3135,7 +3180,8 @@ elektron_upload_data_sample (struct backend *backend, const gchar *path, { gint err; guint id; - struct idata data_sample; + GSList *list; + struct idata data_sample, data_footer; err = common_slot_get_id_from_path (path, &id); if (err) @@ -3146,7 +3192,8 @@ elektron_upload_data_sample (struct backend *backend, const gchar *path, control->parts = 2; control->part = 0; - err = elektron_set_data_sample_from_sample (&data_sample, sample, id); + err = elektron_set_data_sample_from_sample (&data_sample, &data_footer, + sample, id); if (err) { return err; @@ -3155,9 +3202,13 @@ elektron_upload_data_sample (struct backend *backend, const gchar *path, task_control_set_progress (control, 1.0); control->part++; - err = elektron_upload_data_prefix (backend, path, &data_sample, control, - FS_DATA_SAMPLES_PREFIX); + list = g_slist_append (NULL, &data_sample); + list = g_slist_append (list, &data_footer); + err = elektron_upload_data_list_prefix (backend, path, list, control, + FS_DATA_SAMPLES_PREFIX); + g_slist_free (list); idata_clear (&data_sample); + idata_clear (&data_footer); return err; } diff --git a/test/tests_elektron.c b/test/tests_elektron.c index 1a7b1126..7fe99ba5 100644 --- a/test/tests_elektron.c +++ b/test/tests_elektron.c @@ -22,6 +22,7 @@ void elektron_destroy_data (struct backend *backend); gint elektron_set_sample_from_data_sample (struct idata *sample, struct idata *data_sample); gint elektron_set_data_sample_from_sample (struct idata *data_sample, + struct idata *data_footer, struct idata *sample, guint slot); static void @@ -195,7 +196,7 @@ static void test_elektron_data_sample () { gint err; - struct idata sample, data_sample; + struct idata sample, data_sample, data_footer; struct task_control task_control; struct sample_info sample_info_src; struct sample_load_opts sample_load_opts; @@ -220,7 +221,8 @@ test_elektron_data_sample () return; } - err = elektron_set_data_sample_from_sample (&data_sample, &sample, 3); + err = elektron_set_data_sample_from_sample (&data_sample, &data_footer, + &sample, 3); CU_ASSERT_EQUAL (err, 0); if (err) { @@ -230,6 +232,8 @@ test_elektron_data_sample () content_before = idata_steal (&sample); + g_byte_array_append (data_sample.content, data_footer.content->data, + data_footer.content->len); err = elektron_set_sample_from_data_sample (&sample, &data_sample); CU_ASSERT_EQUAL (err, 0); if (err) @@ -237,8 +241,8 @@ test_elektron_data_sample () idata_clear (&data_sample); return; } - idata_clear (&data_sample); + idata_clear (&data_footer); content_after = idata_steal (&sample); From dc4277e4f7bf7d4c745b269845d6c3d7b7f41f62 Mon Sep 17 00:00:00 2001 From: dagargo Date: Thu, 19 Mar 2026 21:18:19 +0100 Subject: [PATCH 19/20] elektron: Extract CRC function --- src/connectors/elektron.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 49b768f3..9bb1e58e 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -1696,6 +1696,12 @@ elektron_new_msg_upgrade_os_start (guint size) return msg; } +static guint32 +elektron_crc (guint8 *data, guint32 len) +{ + return crc32 (0xffffffff, data, len); +} + static GByteArray * elektron_new_msg_upgrade_os_write (GByteArray *os_data, gint *offset) { @@ -1714,7 +1720,7 @@ elektron_new_msg_upgrade_os_write (GByteArray *os_data, gint *offset) len = os_data->len - *offset; } - crc = crc32 (0xffffffff, &os_data->data[*offset], len); + crc = elektron_crc (&os_data->data[*offset], len); debug_print (2, "CRC: %0x", crc); @@ -2676,9 +2682,8 @@ elektron_set_sample_from_data_sample (struct idata *sample, guint crc_start = sizeof (struct elektron_data_header); guint crc_size = sizeof (struct elektron_data_sample_slot_header) + size; - guint32 crc = crc32 (0xffffffff, - &data_sample->content->data[crc_start], - crc_size); + guint32 crc = elektron_crc (&data_sample->content->data[crc_start], + crc_size); debug_print (1, "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x", crc_size, crc); @@ -2783,8 +2788,8 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, guint crc_start = sizeof (struct elektron_data_header); guint crc_size = sizeof (struct elektron_data_sample_slot_header) + sample->content->len; - guint32 crc = crc32 (0xffffffff, (guint8 *) & sample_content->data[crc_start], - crc_size); + guint32 crc = elektron_crc ((guint8 *) & sample_content->data[crc_start], + crc_size); debug_print (1, "Sample data size: %u bytes, CRC (slot_header+data): 0x%08x", crc_size, crc); @@ -3050,7 +3055,7 @@ elektron_upload_data_list_prefix (struct backend *backend, const gchar *path, len = content->len - offset; } - crc = crc32 (0xffffffff, &content->data[offset], len); + crc = elektron_crc (&content->data[offset], len); aux32 = g_htonl (crc); g_byte_array_append (tx_msg, (guint8 *) & aux32, sizeof (guint32)); From e2a95598480bcedc02b7f537fd5a6a87886cf204 Mon Sep 17 00:00:00 2001 From: Gautier Date: Sat, 21 Mar 2026 11:26:12 +0100 Subject: [PATCH 20/20] elektron: slot_header.name is not zero-terminated --- src/connectors/elektron.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/connectors/elektron.c b/src/connectors/elektron.c index 9bb1e58e..4843ef32 100644 --- a/src/connectors/elektron.c +++ b/src/connectors/elektron.c @@ -2695,9 +2695,9 @@ elektron_set_sample_from_data_sample (struct idata *sample, return -1; } - gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN]; + gchar safe_name[ELEKTRON_DATA_SAMPLE_MAX_LEN + 1]; snprintf (safe_name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", - ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, slot_header->name); + ELEKTRON_DATA_SAMPLE_MAX_LEN, slot_header->name); GByteArray *content = g_byte_array_sized_new (size); content->len = size; @@ -2766,8 +2766,8 @@ elektron_set_data_sample_from_sample (struct idata *data_sample, slot_header.length = GINT32_TO_BE (frames); gchar *name = elektron_get_cp1252 (sample->name); - snprintf (slot_header.name, ELEKTRON_DATA_SAMPLE_MAX_LEN, "%.*s", - ELEKTRON_DATA_SAMPLE_MAX_LEN - 1, name); + memcpy (slot_header.name, name, + MIN (strlen (name), ELEKTRON_DATA_SAMPLE_MAX_LEN)); g_free (name); g_byte_array_append (sample_content, (guint8 *) & header,