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
#define ALSACONTROLLER_H
#include <string>
#include <optional>
#include <string>
#include <vector>
class AlsaController
{
class AlsaController {
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;
int getCardNumber() const;

View File

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

View File

@ -121,7 +121,6 @@ void tascam_free_urbs(struct tascam_card *tascam) {
}
}
kfree(tascam->capture_routing_buffer);
tascam->capture_routing_buffer = NULL;
kfree(tascam->capture_decode_dst_block);
@ -169,10 +168,10 @@ int tascam_alloc_urbs(struct tascam_card *tascam) {
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++) {
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)
goto error;
@ -264,8 +263,6 @@ int tascam_alloc_urbs(struct tascam_card *tascam) {
if (!tascam->capture_decode_dst_block)
goto error;
tascam->capture_routing_buffer =
kmalloc(FRAMES_PER_DECODE_BLOCK * DECODED_CHANNELS_PER_FRAME *
DECODED_SAMPLE_SIZE,

View File

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

View File

@ -192,10 +192,12 @@ void tascam_capture_work_handler(struct work_struct *work) {
if (can_process) {
size_t bytes_to_end = CAPTURE_RING_BUFFER_SIZE - read_ptr;
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 {
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 =
(read_ptr + RAW_BYTES_PER_DECODE_BLOCK) % CAPTURE_RING_BUFFER_SIZE;
@ -287,4 +289,3 @@ void capture_urb_complete(struct urb *urb) {
out:
usb_put_urb(urb);
}

View File

@ -3,6 +3,68 @@
#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
*
@ -15,28 +77,6 @@
* which helps the driver adjust the packet size dynamically to match the
* 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 = {
.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;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
switch (rate) {
case 44100:
tascam->feedback_patterns = patterns_44khz;
tascam->feedback_base_value = 43;
tascam->feedback_max_value = 45;
break;
case 48000:
tascam->feedback_patterns = patterns_48khz;
tascam->feedback_base_value = 47;
tascam->feedback_max_value = 49;
break;
case 88200:
tascam->feedback_patterns = patterns_88khz;
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;
tascam->fpo.sample_rate_khz = rate / 1000;
tascam->fpo.base_feedback_value = tascam->fpo.sample_rate_khz;
tascam->fpo.feedback_offset = 2;
tascam->fpo.current_index = 0;
tascam->fpo.previous_index = 0;
tascam->fpo.sync_locked = false;
unsigned int initial_value = tascam->fpo.sample_rate_khz / 8;
for (int i = 0; i < 5; i++) {
int target_sum =
tascam->fpo.sample_rate_khz - tascam->fpo.feedback_offset + i;
fpoInitPattern(8, tascam->fpo.full_frame_patterns[i], initial_value,
target_sum);
}
}

View File

@ -55,7 +55,6 @@ static int tascam_playback_prepare(struct snd_pcm_substream *substream) {
int i, u;
size_t nominal_frames_per_packet, nominal_bytes_per_packet;
size_t total_bytes_in_urb;
unsigned int feedback_packets;
tascam->driver_playback_pos = 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++)
tascam->feedback_accumulator_pattern[i] = nominal_frames_per_packet;
feedback_packets = 1;
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
struct urb *f_urb = tascam->feedback_urbs[i];
int j;
f_urb->number_of_packets = feedback_packets;
f_urb->transfer_buffer_length = feedback_packets * FEEDBACK_PACKET_SIZE;
for (j = 0; j < feedback_packets; j++) {
f_urb->number_of_packets = FEEDBACK_URB_PACKETS;
f_urb->transfer_buffer_length = FEEDBACK_URB_PACKETS * FEEDBACK_PACKET_SIZE;
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].length = FEEDBACK_PACKET_SIZE;
}
@ -221,10 +218,15 @@ void playback_urb_complete(struct urb *urb) {
frames_to_bytes(runtime, runtime->buffer_size - offset_frames);
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 + first_chunk_bytes, runtime->dma_area, second_chunk_bytes);
memcpy(dst_buf,
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 {
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 */
@ -308,11 +310,20 @@ void feedback_urb_complete(struct urb *urb) {
feedback_value =
*((u8 *)urb->transfer_buffer + urb->iso_frame_desc[p].offset);
if (packet_ok && feedback_value >= tascam->feedback_base_value &&
feedback_value <= tascam->feedback_max_value) {
pattern =
tascam
->feedback_patterns[feedback_value - tascam->feedback_base_value];
if (packet_ok) {
int delta = feedback_value - tascam->fpo.base_feedback_value +
tascam->fpo.feedback_offset;
int pattern_idx;
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;
int i;
@ -418,4 +429,3 @@ unlock_and_continue:
out:
usb_put_urb(urb);
}