feat: Implement dynamic feedback and align URB configuration

This commit is contained in:
Šerif Rami 2025-08-07 10:23:05 +02:00
parent d35da0e547
commit cc31e81f60
8 changed files with 159 additions and 116 deletions

View File

@ -1,14 +1,14 @@
#ifndef ALSACONTROLLER_H #ifndef ALSACONTROLLER_H
#define ALSACONTROLLER_H #define ALSACONTROLLER_H
#include <string>
#include <optional> #include <optional>
#include <string>
#include <vector> #include <vector>
class AlsaController class AlsaController {
{
public: public:
AlsaController(const std::vector<std::string>& target_card_names = {"US-144MKII", "US-144"}); AlsaController(const std::vector<std::string> &target_card_names = {
"US-144MKII", "US-144"});
std::optional<std::string> getCardId() const; std::optional<std::string> getCardId() const;
int getCardNumber() const; int getCardNumber() const;

View File

@ -1,18 +1,17 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QWidget>
#include <QPixmap>
#include <QMap>
#include "alsacontroller.h" #include "alsacontroller.h"
#include <QMap>
#include <QPixmap>
#include <QWidget>
class QLabel; class QLabel;
class QComboBox; class QComboBox;
class QPushButton; class QPushButton;
class QDialog; class QDialog;
class MainWindow : public QWidget class MainWindow : public QWidget {
{
Q_OBJECT Q_OBJECT
public: public:
@ -24,11 +23,13 @@ protected:
private: private:
void initUi(); void initUi();
void loadDynamicSettings(); void loadDynamicSettings();
std::pair<QWidget*, QComboBox*> createControlWidget(const QString& labelText, const QStringList& items); std::pair<QWidget *, QComboBox *>
createControlWidget(const QString &labelText, const QStringList &items);
void updateCombo(QComboBox *combo, const std::string &controlName); void updateCombo(QComboBox *combo, const std::string &controlName);
private slots: private slots:
void onControlChanged(const std::string& controlName, int index, QComboBox* combo); void onControlChanged(const std::string &controlName, int index,
QComboBox *combo);
void showAboutDialog(); void showAboutDialog();
private: private:

View File

@ -121,7 +121,6 @@ void tascam_free_urbs(struct tascam_card *tascam) {
} }
} }
kfree(tascam->capture_routing_buffer); kfree(tascam->capture_routing_buffer);
tascam->capture_routing_buffer = NULL; tascam->capture_routing_buffer = NULL;
kfree(tascam->capture_decode_dst_block); kfree(tascam->capture_decode_dst_block);
@ -169,10 +168,10 @@ int tascam_alloc_urbs(struct tascam_card *tascam) {
urb->complete = playback_urb_complete; urb->complete = playback_urb_complete;
} }
tascam->feedback_urb_alloc_size = FEEDBACK_PACKET_SIZE * MAX_FEEDBACK_PACKETS; tascam->feedback_urb_alloc_size = FEEDBACK_PACKET_SIZE * FEEDBACK_URB_PACKETS;
for (i = 0; i < NUM_FEEDBACK_URBS; i++) { for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
struct urb *f_urb = usb_alloc_urb(MAX_FEEDBACK_PACKETS, GFP_KERNEL); struct urb *f_urb = usb_alloc_urb(FEEDBACK_URB_PACKETS, GFP_KERNEL);
if (!f_urb) if (!f_urb)
goto error; goto error;
@ -264,8 +263,6 @@ int tascam_alloc_urbs(struct tascam_card *tascam) {
if (!tascam->capture_decode_dst_block) if (!tascam->capture_decode_dst_block)
goto error; goto error;
tascam->capture_routing_buffer = tascam->capture_routing_buffer =
kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME * kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
DECODED_SAMPLE_SIZE, DECODED_SAMPLE_SIZE,

View File

@ -5,9 +5,9 @@
#define __US144MKII_H #define __US144MKII_H
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/timer.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/timer.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/initval.h> #include <sound/initval.h>
@ -71,10 +71,10 @@ enum tascam_register {
#define REG_VAL_ENABLE 0x0101 #define REG_VAL_ENABLE 0x0101
/* --- URB Configuration --- */ /* --- URB Configuration --- */
#define NUM_PLAYBACK_URBS 8 #define NUM_PLAYBACK_URBS 4
#define PLAYBACK_URB_PACKETS 4 #define PLAYBACK_URB_PACKETS 8
#define NUM_FEEDBACK_URBS 4 #define NUM_FEEDBACK_URBS 4
#define MAX_FEEDBACK_PACKETS 5 #define FEEDBACK_URB_PACKETS 1
#define FEEDBACK_PACKET_SIZE 3 #define FEEDBACK_PACKET_SIZE 3
#define NUM_CAPTURE_URBS 8 #define NUM_CAPTURE_URBS 8
#define CAPTURE_URB_SIZE 512 #define CAPTURE_URB_SIZE 512
@ -168,10 +168,7 @@ enum tascam_register {
* @feedback_consecutive_errors: Counter for consecutive feedback errors. * @feedback_consecutive_errors: Counter for consecutive feedback errors.
* @feedback_urb_skip_count: Number of feedback URBs to skip initially for * @feedback_urb_skip_count: Number of feedback URBs to skip initially for
* stabilization. * stabilization.
* @feedback_patterns: Pointer to the current feedback patterns based on sample * @fpo: Holds the state for the dynamic feedback pattern generation.
* rate.
* @feedback_base_value: Base value for feedback pattern lookup.
* @feedback_max_value: Max value for feedback pattern lookup.
* *
* @playback_anchor: USB anchor for playback URBs. * @playback_anchor: USB anchor for playback URBs.
* @capture_anchor: USB anchor for capture URBs. * @capture_anchor: USB anchor for capture URBs.
@ -203,9 +200,16 @@ struct tascam_card {
bool feedback_synced; bool feedback_synced;
unsigned int feedback_consecutive_errors; unsigned int feedback_consecutive_errors;
unsigned int feedback_urb_skip_count; unsigned int feedback_urb_skip_count;
const unsigned int (*feedback_patterns)[8];
unsigned int feedback_base_value; struct us144mkii_frame_pattern_observer {
unsigned int feedback_max_value; unsigned int sample_rate_khz;
unsigned int base_feedback_value;
int feedback_offset;
unsigned int full_frame_patterns[5][8];
unsigned int current_index;
unsigned int previous_index;
bool sync_locked;
} fpo;
// MIDI state (frequently accessed in MIDI handlers) // MIDI state (frequently accessed in MIDI handlers)
atomic_t midi_in_active; atomic_t midi_in_active;

View File

@ -192,10 +192,12 @@ void tascam_capture_work_handler(struct work_struct *work) {
if (can_process) { if (can_process) {
size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - read_ptr; size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - read_ptr;
if (bytes_to_end >= RAW_BYTES_PER_DECODE_BLOCK) { if (bytes_to_end >= RAW_BYTES_PER_DECODE_BLOCK) {
memcpy(raw_block, tascam->capture_ring_buffer + read_ptr, RAW_BYTES_PER_DECODE_BLOCK); memcpy(raw_block, tascam->capture_ring_buffer + read_ptr,
RAW_BYTES_PER_DECODE_BLOCK);
} else { } else {
memcpy(raw_block, tascam->capture_ring_buffer + read_ptr, bytes_to_end); memcpy(raw_block, tascam->capture_ring_buffer + read_ptr, bytes_to_end);
memcpy(raw_block + bytes_to_end, tascam->capture_ring_buffer, RAW_BYTES_PER_DECODE_BLOCK - bytes_to_end); memcpy(raw_block + bytes_to_end, tascam->capture_ring_buffer,
RAW_BYTES_PER_DECODE_BLOCK - bytes_to_end);
} }
tascam->capture_ring_buffer_read_ptr = tascam->capture_ring_buffer_read_ptr =
(read_ptr + RAW_BYTES_PER_DECODE_BLOCK) % CAPTURE_RING_BUFFER_SIZE; (read_ptr + RAW_BYTES_PER_DECODE_BLOCK) % CAPTURE_RING_BUFFER_SIZE;
@ -287,4 +289,3 @@ void capture_urb_complete(struct urb *urb) {
out: out:
usb_put_urb(urb); usb_put_urb(urb);
} }

View File

@ -3,6 +3,68 @@
#include "us144mkii.h" #include "us144mkii.h"
/**
* fpoInitPattern() - Generates a packet distribution pattern.
* @size: The number of elements in the pattern array (e.g., 8).
* @pattern_array: Pointer to the array to be populated.
* @initial_value: The base value to initialize each element with.
* @target_sum: The desired sum of all elements in the final array.
*
* This function initializes an array with a base value and then iteratively
* adjusts the elements to match a target sum, distributing the difference
* as evenly as possible.
*/
static void fpoInitPattern(unsigned int size, unsigned int *pattern_array,
unsigned int initial_value, int target_sum) {
unsigned int current_sum;
int diff;
int abs_diff;
unsigned int stride;
unsigned int i;
if (!size)
return;
/* 1. Initialize the array with the base value. */
current_sum = 0;
for (i = 0; i < size; ++i) {
pattern_array[i] = initial_value;
}
current_sum = size * initial_value;
/* 2. Iteratively adjust until the sum is correct. */
while (current_sum != target_sum) {
diff = target_sum - current_sum;
abs_diff = (diff > 0) ? diff : -diff;
if (abs_diff == 0)
break;
/* Calculate the stride to distribute the adjustments. */
stride = size / abs_diff;
if (stride == 0) {
/* This would happen if the difference is larger than the array
* size, which indicates a problem. The original code breaks
* here.
*/
break;
}
/* Apply the adjustments. */
for (i = 0; i < size; i += stride) {
if (diff > 0)
pattern_array[i]++;
else
pattern_array[i]--;
}
/* Recalculate the sum for the next iteration. */
current_sum = 0;
for (i = 0; i < size; ++i)
current_sum += pattern_array[i];
}
}
/** /**
* @brief Rate-to-Packet Fixing Data * @brief Rate-to-Packet Fixing Data
* *
@ -15,28 +77,6 @@
* which helps the driver adjust the packet size dynamically to match the * which helps the driver adjust the packet size dynamically to match the
* device's consumption rate. * device's consumption rate.
*/ */
static const unsigned int patterns_48khz[5][8] = {{5, 6, 6, 6, 6, 6, 6, 6},
{6, 6, 6, 6, 6, 6, 6, 6},
{6, 6, 6, 6, 6, 6, 6, 6},
{6, 6, 6, 7, 6, 6, 6, 6},
{7, 6, 6, 7, 6, 6, 7, 6}};
static const unsigned int patterns_96khz[5][8] = {
{11, 12, 12, 12, 12, 12, 12, 12},
{12, 12, 12, 12, 12, 12, 12, 12},
{12, 12, 12, 12, 12, 12, 12, 12},
{12, 12, 13, 12, 12, 12, 12, 12},
{13, 12, 12, 13, 12, 12, 13, 12}};
static const unsigned int patterns_88khz[5][8] = {
{10, 11, 11, 11, 11, 11, 11, 11},
{11, 11, 11, 11, 11, 11, 11, 11},
{11, 11, 11, 11, 11, 11, 11, 11},
{11, 11, 12, 11, 11, 11, 11, 11},
{12, 11, 11, 12, 11, 11, 12, 11}};
static const unsigned int patterns_44khz[5][8] = {{5, 5, 5, 5, 5, 5, 5, 6},
{5, 5, 5, 6, 5, 5, 5, 6},
{5, 5, 6, 5, 6, 5, 5, 6},
{5, 6, 5, 6, 5, 6, 5, 6},
{6, 6, 6, 6, 6, 6, 6, 5}};
const struct snd_pcm_hardware tascam_pcm_hw = { const struct snd_pcm_hardware tascam_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
@ -256,29 +296,19 @@ int tascam_pcm_hw_params(struct snd_pcm_substream *substream,
return err; return err;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
switch (rate) { tascam->fpo.sample_rate_khz = rate / 1000;
case 44100: tascam->fpo.base_feedback_value = tascam->fpo.sample_rate_khz;
tascam->feedback_patterns = patterns_44khz; tascam->fpo.feedback_offset = 2;
tascam->feedback_base_value = 43; tascam->fpo.current_index = 0;
tascam->feedback_max_value = 45; tascam->fpo.previous_index = 0;
break; tascam->fpo.sync_locked = false;
case 48000:
tascam->feedback_patterns = patterns_48khz; unsigned int initial_value = tascam->fpo.sample_rate_khz / 8;
tascam->feedback_base_value = 47; for (int i = 0; i < 5; i++) {
tascam->feedback_max_value = 49; int target_sum =
break; tascam->fpo.sample_rate_khz - tascam->fpo.feedback_offset + i;
case 88200: fpoInitPattern(8, tascam->fpo.full_frame_patterns[i], initial_value,
tascam->feedback_patterns = patterns_88khz; target_sum);
tascam->feedback_base_value = 87;
tascam->feedback_max_value = 89;
break;
case 96000:
tascam->feedback_patterns = patterns_96khz;
tascam->feedback_base_value = 95;
tascam->feedback_max_value = 97;
break;
default:
return -EINVAL;
} }
} }

View File

@ -55,7 +55,6 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream) {
int i, u; int i, u;
size_t nominal_frames_per_packet, nominal_bytes_per_packet; size_t nominal_frames_per_packet, nominal_bytes_per_packet;
size_t total_bytes_in_urb; size_t total_bytes_in_urb;
unsigned int feedback_packets;
tascam->driver_playback_pos = 0; tascam->driver_playback_pos = 0;
tascam->playback_frames_consumed = 0; tascam->playback_frames_consumed = 0;
@ -70,15 +69,13 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream) {
for (i = 0; i < FEEDBACK_ACCUMULATOR_SIZE; i++) for (i = 0; i < FEEDBACK_ACCUMULATOR_SIZE; i++)
tascam->feedback_accumulator_pattern[i] = nominal_frames_per_packet; tascam->feedback_accumulator_pattern[i] = nominal_frames_per_packet;
feedback_packets = 1;
for (i = 0; i < NUM_FEEDBACK_URBS; i++) { for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
struct urb *f_urb = tascam->feedback_urbs[i]; struct urb *f_urb = tascam->feedback_urbs[i];
int j; int j;
f_urb->number_of_packets = feedback_packets; f_urb->number_of_packets = FEEDBACK_URB_PACKETS;
f_urb->transfer_buffer_length = feedback_packets * FEEDBACK_PACKET_SIZE; f_urb->transfer_buffer_length = FEEDBACK_URB_PACKETS * FEEDBACK_PACKET_SIZE;
for (j = 0; j < feedback_packets; j++) { for (j = 0; j < FEEDBACK_URB_PACKETS; j++) {
f_urb->iso_frame_desc[j].offset = j * FEEDBACK_PACKET_SIZE; f_urb->iso_frame_desc[j].offset = j * FEEDBACK_PACKET_SIZE;
f_urb->iso_frame_desc[j].length = FEEDBACK_PACKET_SIZE; f_urb->iso_frame_desc[j].length = FEEDBACK_PACKET_SIZE;
} }
@ -221,10 +218,15 @@ void playback_urb_complete(struct urb *urb) {
frames_to_bytes(runtime, runtime->buffer_size - offset_frames); frames_to_bytes(runtime, runtime->buffer_size - offset_frames);
size_t second_chunk_bytes = total_bytes_for_urb - first_chunk_bytes; size_t second_chunk_bytes = total_bytes_for_urb - first_chunk_bytes;
memcpy(dst_buf, runtime->dma_area + frames_to_bytes(runtime, offset_frames), first_chunk_bytes); memcpy(dst_buf,
memcpy(dst_buf + first_chunk_bytes, runtime->dma_area, second_chunk_bytes); runtime->dma_area + frames_to_bytes(runtime, offset_frames),
first_chunk_bytes);
memcpy(dst_buf + first_chunk_bytes, runtime->dma_area,
second_chunk_bytes);
} else { } else {
memcpy(dst_buf, runtime->dma_area + frames_to_bytes(runtime, offset_frames), total_bytes_for_urb); memcpy(dst_buf,
runtime->dma_area + frames_to_bytes(runtime, offset_frames),
total_bytes_for_urb);
} }
/* Apply routing to the contiguous data in our routing buffer */ /* Apply routing to the contiguous data in our routing buffer */
@ -308,11 +310,20 @@ void feedback_urb_complete(struct urb *urb) {
feedback_value = feedback_value =
*((u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset); *((u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset);
if (packet_ok && feedback_value >= tascam->feedback_base_value && if (packet_ok) {
feedback_value <= tascam->feedback_max_value) { int delta = feedback_value - tascam->fpo.base_feedback_value +
pattern = tascam->fpo.feedback_offset;
tascam int pattern_idx;
->feedback_patterns[feedback_value - tascam->feedback_base_value];
if (delta < 0) {
pattern_idx = 0; // Clamp to the lowest pattern
} else if (delta >= 5) {
pattern_idx = 4; // Clamp to the highest pattern
} else {
pattern_idx = delta;
}
pattern = tascam->fpo.full_frame_patterns[pattern_idx];
tascam->feedback_consecutive_errors = 0; tascam->feedback_consecutive_errors = 0;
int i; int i;
@ -418,4 +429,3 @@ unlock_and_continue:
out: out:
usb_put_urb(urb); usb_put_urb(urb);
} }