Minor cleanup. Added Independent Left/Right volume control.
This commit is contained in:
parent
d7d40af360
commit
19c9cdd0f1
|
@ -46,15 +46,21 @@
|
||||||
i2s_obj_t i2s_write_obj;
|
i2s_obj_t i2s_write_obj;
|
||||||
static uint8_t *userbuf;
|
static uint8_t *userbuf;
|
||||||
|
|
||||||
bqf_coeff_t bqf_filters_left[FILTER_STAGES];
|
bqf_coeff_t bqf_filters_left[MAX_FILTER_STAGES];
|
||||||
bqf_coeff_t bqf_filters_right[FILTER_STAGES];
|
bqf_coeff_t bqf_filters_right[MAX_FILTER_STAGES];
|
||||||
bqf_mem_t bqf_filters_mem_left[FILTER_STAGES];
|
bqf_mem_t bqf_filters_mem_left[MAX_FILTER_STAGES];
|
||||||
bqf_mem_t bqf_filters_mem_right[FILTER_STAGES];
|
bqf_mem_t bqf_filters_mem_right[MAX_FILTER_STAGES];
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
uint32_t freq;
|
uint32_t freq;
|
||||||
int16_t volume;
|
union {
|
||||||
int16_t target_volume;
|
int16_t volume[2];
|
||||||
|
int32_t _volume;
|
||||||
|
};
|
||||||
|
union {
|
||||||
|
int16_t target_volume[2];
|
||||||
|
int32_t _target_volume;
|
||||||
|
};
|
||||||
bool mute;
|
bool mute;
|
||||||
} audio_state = {
|
} audio_state = {
|
||||||
.freq = 48000,
|
.freq = 48000,
|
||||||
|
@ -153,20 +159,18 @@ void core1_entry() {
|
||||||
// Update the volume if required. We do this from core1 as
|
// Update the volume if required. We do this from core1 as
|
||||||
// core0 is more heavily loaded, doing this from core0 can
|
// core0 is more heavily loaded, doing this from core0 can
|
||||||
// lead to audio crackling.
|
// lead to audio crackling.
|
||||||
if (audio_state.volume != audio_state.target_volume) {
|
if (audio_state._volume != audio_state._target_volume) {
|
||||||
// Volume attenuation:
|
// PCM3060 volume attenuation:
|
||||||
// 0: 0db (default)
|
// 0: 0db (default)
|
||||||
// 55: -100db
|
// 55: -100db
|
||||||
// 56..: Mute
|
// 56..: Mute
|
||||||
uint8_t value = 255 + (audio_state.target_volume / 128);
|
|
||||||
|
|
||||||
uint8_t buf[3];
|
uint8_t buf[3];
|
||||||
buf[0] = 65; // register addr
|
buf[0] = 65; // register addr
|
||||||
buf[1] = value; // data left
|
buf[1] = 255 + (audio_state.target_volume[0] / 128); // data left
|
||||||
buf[2] = value; // data right
|
buf[2] = 255 + (audio_state.target_volume[1] / 128); // data right
|
||||||
i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 3, false);
|
i2c_write_blocking(i2c0, PCM_I2C_ADDR, buf, 3, false);
|
||||||
|
|
||||||
audio_state.volume = audio_state.target_volume;
|
audio_state._volume = audio_state._target_volume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,9 +339,9 @@ static const audio_device_config ad_conf = {
|
||||||
.bSourceID = 1,
|
.bSourceID = 1,
|
||||||
.bControlSize = 1,
|
.bControlSize = 1,
|
||||||
.bmaControls = {
|
.bmaControls = {
|
||||||
AUDIO_FEATURE_MUTE | AUDIO_FEATURE_VOLUME,
|
AUDIO_FEATURE_MUTE, // Master channel
|
||||||
0,
|
AUDIO_FEATURE_VOLUME, // Left channel
|
||||||
0
|
AUDIO_FEATURE_VOLUME, // Right channel
|
||||||
},
|
},
|
||||||
.iFeature = 0,
|
.iFeature = 0,
|
||||||
},
|
},
|
||||||
|
@ -521,7 +525,17 @@ static bool do_get_current(struct usb_setup_packet *setup) {
|
||||||
}
|
}
|
||||||
case 2: { // volume
|
case 2: { // volume
|
||||||
/* Current volume. See UAC Spec 1.0 p.77 */
|
/* Current volume. See UAC Spec 1.0 p.77 */
|
||||||
usb_start_tiny_control_in_transfer(audio_state.volume, 2);
|
const uint8_t cn = (uint8_t) setup->wValue;
|
||||||
|
if (cn == AUDIO_CHANNEL_LEFT_FRONT) {
|
||||||
|
usb_start_tiny_control_in_transfer(audio_state.target_volume[0], 2);
|
||||||
|
}
|
||||||
|
else if (cn == AUDIO_CHANNEL_RIGHT_FRONT) {
|
||||||
|
usb_start_tiny_control_in_transfer(audio_state.target_volume[1], 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -590,7 +604,7 @@ static void _audio_reconfigure() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_set_volume(int16_t volume) {
|
static void audio_set_volume(int8_t channel, int16_t volume) {
|
||||||
// volume is in the range 127.9961dB (0x7FFF) .. -127.9961dB (0x8001). 0x8000 = mute
|
// volume is in the range 127.9961dB (0x7FFF) .. -127.9961dB (0x8001). 0x8000 = mute
|
||||||
// the old code reported a min..max volume of -90.9961dB (0xA500) .. 0dB (0x0)
|
// the old code reported a min..max volume of -90.9961dB (0xA500) .. 0dB (0x0)
|
||||||
|
|
||||||
|
@ -603,12 +617,21 @@ static void audio_set_volume(int16_t volume) {
|
||||||
else if (volume < (int16_t) MIN_VOLUME) {
|
else if (volume < (int16_t) MIN_VOLUME) {
|
||||||
volume = MIN_VOLUME;
|
volume = MIN_VOLUME;
|
||||||
}
|
}
|
||||||
audio_state.target_volume = volume;
|
if (channel == AUDIO_CHANNEL_LEFT_FRONT || channel == 0) {
|
||||||
|
audio_state.target_volume[0] = volume;
|
||||||
|
}
|
||||||
|
if (channel == AUDIO_CHANNEL_RIGHT_FRONT || channel == 0) {
|
||||||
|
audio_state.target_volume[1] = volume;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_cmd_packet(struct usb_endpoint *ep) {
|
static void audio_cmd_packet(struct usb_endpoint *ep) {
|
||||||
assert(audio_control_cmd_t.cmd == AUDIO_REQ_SetCurrent);
|
assert(audio_control_cmd_t.cmd == AUDIO_REQ_SetCurrent);
|
||||||
struct usb_buffer *buffer = usb_current_out_packet_buffer(ep);
|
struct usb_buffer *buffer = usb_current_out_packet_buffer(ep);
|
||||||
|
|
||||||
|
// printf("%s: CMD: %u, Type: %u, CS: %u, CN: %u, Unit: %u, Len: %u\n", __PRETTY_FUNCTION__, audio_control_cmd_t.cmd, audio_control_cmd_t.type,
|
||||||
|
// audio_control_cmd_t.cs, audio_control_cmd_t.cn, audio_control_cmd_t.unit, audio_control_cmd_t.len);
|
||||||
|
|
||||||
audio_control_cmd_t.cmd = 0;
|
audio_control_cmd_t.cmd = 0;
|
||||||
if (buffer->data_len >= audio_control_cmd_t.len) {
|
if (buffer->data_len >= audio_control_cmd_t.len) {
|
||||||
if (audio_control_cmd_t.type == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
|
if (audio_control_cmd_t.type == USB_REQ_TYPE_RECIPIENT_INTERFACE) {
|
||||||
|
@ -622,7 +645,7 @@ static void audio_cmd_packet(struct usb_endpoint *ep) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: { // volume
|
case 2: { // volume
|
||||||
audio_set_volume(*(int16_t *) buffer->data);
|
audio_set_volume(audio_control_cmd_t.cn, *(int16_t *) buffer->data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -763,7 +786,7 @@ void usb_sound_card_init() {
|
||||||
assert(device);
|
assert(device);
|
||||||
|
|
||||||
device->setup_request_handler = ad_setup_request_handler;
|
device->setup_request_handler = ad_setup_request_handler;
|
||||||
audio_set_volume(DEFAULT_VOLUME);
|
audio_set_volume(0, DEFAULT_VOLUME);
|
||||||
_audio_reconfigure();
|
_audio_reconfigure();
|
||||||
|
|
||||||
usb_device_start();
|
usb_device_start();
|
||||||
|
|
|
@ -125,7 +125,7 @@ static bool do_get_minimum(struct usb_setup_packet *);
|
||||||
static bool do_get_maximum(struct usb_setup_packet *);
|
static bool do_get_maximum(struct usb_setup_packet *);
|
||||||
static bool do_get_resolution(struct usb_setup_packet *);
|
static bool do_get_resolution(struct usb_setup_packet *);
|
||||||
static void _audio_reconfigure(void);
|
static void _audio_reconfigure(void);
|
||||||
static void audio_set_volume(int16_t);
|
static void audio_set_volume(int8_t, int16_t);
|
||||||
static void audio_cmd_packet(struct usb_endpoint *);
|
static void audio_cmd_packet(struct usb_endpoint *);
|
||||||
static bool as_set_alternate(struct usb_interface *, uint);
|
static bool as_set_alternate(struct usb_interface *, uint);
|
||||||
static bool do_set_current(struct usb_setup_packet *);
|
static bool do_set_current(struct usb_setup_packet *);
|
||||||
|
|
|
@ -19,47 +19,46 @@
|
||||||
#include "bqf.h"
|
#include "bqf.h"
|
||||||
#include "run.h"
|
#include "run.h"
|
||||||
|
|
||||||
|
int FILTER_STAGES = 0;
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Here is where your digital signal processing journey begins. Follow this
|
* Here is where your digital signal processing journey begins. Follow this
|
||||||
* guide, and don't forget any steps!
|
* guide, and don't forget any steps!
|
||||||
*
|
*
|
||||||
* 1. Go to user.h and change FILTER_STAGES to the number of filter stages you
|
* 1. Define the filters that you want to use. Check out "bqf.c" for a
|
||||||
* want.
|
|
||||||
* 2. Define the filters that you want to use. Check out "bqf.c" for a
|
|
||||||
* complete list of what they are and how they work. Using those filters, you
|
* complete list of what they are and how they work. Using those filters, you
|
||||||
* can create ANY digital signal shape you want. Anything you can dream of.
|
* can create ANY digital signal shape you want. Anything you can dream of.
|
||||||
* 3. You're done! Enjoy the sounds of anything you want.
|
* 2. You're done! Enjoy the sounds of anything you want.
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
void define_filters() {
|
void define_filters() {
|
||||||
|
|
||||||
// First filter.
|
// First filter.
|
||||||
bqf_memreset(&bqf_filters_mem_left[0]);
|
bqf_memreset(&bqf_filters_mem_left[FILTER_STAGES]);
|
||||||
bqf_memreset(&bqf_filters_mem_right[0]);
|
bqf_memreset(&bqf_filters_mem_right[FILTER_STAGES]);
|
||||||
bqf_peaking_config(SAMPLING_FREQ, 38.0, -19.0, 0.9, &bqf_filters_left[0]);
|
bqf_peaking_config(SAMPLING_FREQ, 38.0, -19.0, 0.9, &bqf_filters_left[FILTER_STAGES]);
|
||||||
bqf_peaking_config(SAMPLING_FREQ, 38.0, -19.0, 0.9, &bqf_filters_right[0]);
|
bqf_peaking_config(SAMPLING_FREQ, 38.0, -19.0, 0.9, &bqf_filters_right[FILTER_STAGES++]);
|
||||||
|
|
||||||
// Second filter.
|
// Second filter.
|
||||||
bqf_memreset(&bqf_filters_mem_left[1]);
|
bqf_memreset(&bqf_filters_mem_left[FILTER_STAGES]);
|
||||||
bqf_memreset(&bqf_filters_mem_right[1]);
|
bqf_memreset(&bqf_filters_mem_right[FILTER_STAGES]);
|
||||||
bqf_lowshelf_config(SAMPLING_FREQ, 2900.0, 3.0, 4.0, &bqf_filters_left[1]);
|
bqf_lowshelf_config(SAMPLING_FREQ, 2900.0, 3.0, 4.0, &bqf_filters_left[FILTER_STAGES]);
|
||||||
bqf_lowshelf_config(SAMPLING_FREQ, 2900.0, 3.0, 4.0, &bqf_filters_right[1]);
|
bqf_lowshelf_config(SAMPLING_FREQ, 2900.0, 3.0, 4.0, &bqf_filters_right[FILTER_STAGES++]);
|
||||||
|
|
||||||
// Third filter.
|
// Third filter.
|
||||||
bqf_memreset(&bqf_filters_mem_left[2]);
|
bqf_memreset(&bqf_filters_mem_left[FILTER_STAGES]);
|
||||||
bqf_memreset(&bqf_filters_mem_right[2]);
|
bqf_memreset(&bqf_filters_mem_right[FILTER_STAGES]);
|
||||||
bqf_peaking_config(SAMPLING_FREQ, 430.0, 6.0, 3.5, &bqf_filters_left[2]);
|
bqf_peaking_config(SAMPLING_FREQ, 430.0, 6.0, 3.5, &bqf_filters_left[FILTER_STAGES]);
|
||||||
bqf_peaking_config(SAMPLING_FREQ, 430.0, 6.0, 3.5, &bqf_filters_right[2]);
|
bqf_peaking_config(SAMPLING_FREQ, 430.0, 6.0, 3.5, &bqf_filters_right[FILTER_STAGES++]);
|
||||||
|
|
||||||
// Fourth filter.
|
// Fourth filter.
|
||||||
bqf_memreset(&bqf_filters_mem_left[3]);
|
bqf_memreset(&bqf_filters_mem_left[FILTER_STAGES]);
|
||||||
bqf_memreset(&bqf_filters_mem_right[3]);
|
bqf_memreset(&bqf_filters_mem_right[FILTER_STAGES]);
|
||||||
bqf_highshelf_config(SAMPLING_FREQ, 8400.0, 3.0, 4.0, &bqf_filters_left[3]);
|
bqf_highshelf_config(SAMPLING_FREQ, 8400.0, 3.0, 4.0, &bqf_filters_left[FILTER_STAGES]);
|
||||||
bqf_highshelf_config(SAMPLING_FREQ, 8400.0, 3.0, 4.0, &bqf_filters_right[3]);
|
bqf_highshelf_config(SAMPLING_FREQ, 8400.0, 3.0, 4.0, &bqf_filters_right[FILTER_STAGES++]);
|
||||||
|
|
||||||
// Fifth filter.
|
// Fifth filter.
|
||||||
bqf_memreset(&bqf_filters_mem_left[4]);
|
bqf_memreset(&bqf_filters_mem_left[FILTER_STAGES]);
|
||||||
bqf_memreset(&bqf_filters_mem_right[4]);
|
bqf_memreset(&bqf_filters_mem_right[FILTER_STAGES]);
|
||||||
bqf_peaking_config(SAMPLING_FREQ, 4800.0, 6.0, 5.0, &bqf_filters_left[4]);
|
bqf_peaking_config(SAMPLING_FREQ, 4800.0, 6.0, 5.0, &bqf_filters_left[FILTER_STAGES]);
|
||||||
bqf_peaking_config(SAMPLING_FREQ, 4800.0, 6.0, 5.0, &bqf_filters_right[4]);
|
bqf_peaking_config(SAMPLING_FREQ, 4800.0, 6.0, 5.0, &bqf_filters_right[FILTER_STAGES++]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,13 @@
|
||||||
|
|
||||||
#include "bqf.h"
|
#include "bqf.h"
|
||||||
|
|
||||||
// todo fix this. people will forget this.
|
#define MAX_FILTER_STAGES 8
|
||||||
#define FILTER_STAGES 5 // Don't forget to set this to the right size!
|
extern int FILTER_STAGES;
|
||||||
|
|
||||||
extern bqf_coeff_t bqf_filters_left[FILTER_STAGES];
|
extern bqf_coeff_t bqf_filters_left[MAX_FILTER_STAGES];
|
||||||
extern bqf_coeff_t bqf_filters_right[FILTER_STAGES];
|
extern bqf_coeff_t bqf_filters_right[MAX_FILTER_STAGES];
|
||||||
extern bqf_mem_t bqf_filters_mem_left[FILTER_STAGES];
|
extern bqf_mem_t bqf_filters_mem_left[MAX_FILTER_STAGES];
|
||||||
extern bqf_mem_t bqf_filters_mem_right[FILTER_STAGES];
|
extern bqf_mem_t bqf_filters_mem_right[MAX_FILTER_STAGES];
|
||||||
|
|
||||||
void define_filters(void);
|
void define_filters(void);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ This is a basic utility for testing the Ploopy headphones filtering routines on
|
||||||
Find a source file and use ffmpeg to convert it to 16bit stereo PCM samples:
|
Find a source file and use ffmpeg to convert it to 16bit stereo PCM samples:
|
||||||
|
|
||||||
```
|
```
|
||||||
ffmpeg -i <input file> -map 0:6 -vn -f s16le -acodec pcm_s16le input.pcm
|
ffmpeg -i <input file> -vn -f s16le -acodec pcm_s16le input.pcm
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `filter_test` to process the PCM samples. The `filter_test` program takes two arguments an input file and an output file:
|
Run `filter_test` to process the PCM samples. The `filter_test` program takes two arguments an input file and an output file:
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
#include "fix16.h"
|
#include "fix16.h"
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
|
|
||||||
bqf_coeff_t bqf_filters_left[FILTER_STAGES];
|
bqf_coeff_t bqf_filters_left[MAX_FILTER_STAGES];
|
||||||
bqf_coeff_t bqf_filters_right[FILTER_STAGES];
|
bqf_coeff_t bqf_filters_right[MAX_FILTER_STAGES];
|
||||||
bqf_mem_t bqf_filters_mem_left[FILTER_STAGES];
|
bqf_mem_t bqf_filters_mem_left[MAX_FILTER_STAGES];
|
||||||
bqf_mem_t bqf_filters_mem_right[FILTER_STAGES];
|
bqf_mem_t bqf_filters_mem_right[MAX_FILTER_STAGES];
|
||||||
|
|
||||||
const char* usage = "Usage: %s INFILE OUTFILE\n\n"
|
const char* usage = "Usage: %s INFILE OUTFILE\n\n"
|
||||||
"Reads 16bit stereo PCM data from INFILE, runs it through the Ploopy headphones\n"
|
"Reads 16bit stereo PCM data from INFILE, runs it through the Ploopy headphones\n"
|
||||||
|
|
Loading…
Reference in New Issue