Merge branch 'cursor/igiRadio-ios-app' into HEAD
This commit is contained in:
@@ -2,6 +2,28 @@
|
|||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x00 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x6C , 0xAC ,
|
0x00 , 0x00 , 0x6C , 0xAC ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x7F , 0xFC , 0xB9 ,
|
||||||
|
0x00 , 0x7F , 0xFC , 0xB9 ,
|
||||||
|
0x00 , 0x7F , 0xFC , 0xB9 ,
|
||||||
|
0x00 , 0x7F , 0xFC , 0xB9 ,
|
||||||
|
0x01 , 0x41 , 0x85 , 0x7F ,
|
||||||
|
0x00 , 0x09 , 0x0F , 0xCC ,
|
||||||
|
0x00 , 0x09 , 0x0F , 0xCC ,
|
||||||
|
0x00 , 0x06 , 0x6A , 0x4A ,
|
||||||
|
0x00 , 0x04 , 0x8A , 0xA7 ,
|
||||||
|
0x00 , 0x03 , 0x37 , 0x18 ,
|
||||||
|
0x00 , 0x02 , 0x46 , 0xB5 ,
|
||||||
|
0x00 , 0x01 , 0x9C , 0x86 ,
|
||||||
|
0x00 , 0x01 , 0x24 , 0x0C ,
|
||||||
|
0x00 , 0x00 , 0xCE , 0xC1 ,
|
||||||
|
0x00 , 0x00 , 0x92 , 0x5F ,
|
||||||
|
0x00 , 0x00 , 0x67 , 0x9F ,
|
||||||
|
0x00 , 0x00 , 0x49 , 0x5C ,
|
||||||
|
0x00 , 0x00 , 0xDA , 0xD7 ,
|
||||||
|
0x00 , 0x02 , 0xFD , 0x86 ,
|
||||||
|
0x00 , 0x0A , 0x75 , 0xDE ,
|
||||||
|
0x00 , 0x24 , 0x97 , 0x68 ,
|
||||||
|
0x00 , 0x7E , 0xDA , 0x4D ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
@@ -10,31 +32,113 @@
|
|||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x00 , 0x0F , 0xB9 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x00 , 0x09 , 0x60 ,
|
||||||
|
0x00 , 0x00 , 0x00 , 0xB6 ,
|
||||||
|
0x00 , 0x00 , 0x4E , 0xB0 ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0xE0 ,
|
||||||
|
0x00 , 0x00 , 0x07 , 0x1C ,
|
||||||
|
0x00 , 0x00 , 0x4E , 0xB0 ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0xE0 ,
|
||||||
|
0x00 , 0x00 , 0x07 , 0x1C ,
|
||||||
|
0x00 , 0x00 , 0x4E , 0xB0 ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0xE0 ,
|
||||||
|
0x00 , 0x00 , 0x07 , 0x1C ,
|
||||||
|
0x00 , 0x00 , 0x4E , 0xB0 ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0xE0 ,
|
||||||
|
0x00 , 0x00 , 0x07 , 0x1C ,
|
||||||
0x00 , 0x7F , 0xC3 , 0x65 ,
|
0x00 , 0x7F , 0xC3 , 0x65 ,
|
||||||
0x0F , 0x00 , 0x79 , 0x35 ,
|
0x0F , 0x00 , 0x79 , 0x35 ,
|
||||||
0x00 , 0x7F , 0xC3 , 0x65 ,
|
0x00 , 0x7F , 0xC3 , 0x65 ,
|
||||||
0x00 , 0xFF , 0x86 , 0xAE ,
|
0x00 , 0xFF , 0x86 , 0xAE ,
|
||||||
0x0F , 0x80 , 0x79 , 0x19 ,
|
0x0F , 0x80 , 0x79 , 0x19 ,
|
||||||
|
0x00 , 0x7F , 0xE6 , 0x6F ,
|
||||||
|
0x0F , 0x01 , 0xD7 , 0xBF ,
|
||||||
|
0x00 , 0x7E , 0x47 , 0xC2 ,
|
||||||
|
0x00 , 0xFE , 0x28 , 0x41 ,
|
||||||
|
0x0F , 0x81 , 0xD1 , 0xCF ,
|
||||||
|
0x00 , 0x80 , 0x0B , 0x93 ,
|
||||||
|
0x0F , 0x06 , 0x4A , 0x08 ,
|
||||||
|
0x00 , 0x79 , 0xF5 , 0x34 ,
|
||||||
|
0x00 , 0xF9 , 0xB5 , 0xF8 ,
|
||||||
|
0x0F , 0x85 , 0xFF , 0x39 ,
|
||||||
|
0x00 , 0x7F , 0x5D , 0x86 ,
|
||||||
|
0x0F , 0x14 , 0xBB , 0x22 ,
|
||||||
|
0x00 , 0x6E , 0x7D , 0xEA ,
|
||||||
|
0x00 , 0xEB , 0x44 , 0xDE ,
|
||||||
|
0x0F , 0x92 , 0x24 , 0x90 ,
|
||||||
|
0x00 , 0x80 , 0x52 , 0x5D ,
|
||||||
|
0x0F , 0x3C , 0xC4 , 0x90 ,
|
||||||
|
0x00 , 0x55 , 0x01 , 0xAB ,
|
||||||
|
0x00 , 0xC3 , 0x3B , 0x70 ,
|
||||||
|
0x0F , 0xAA , 0xAB , 0xF8 ,
|
||||||
|
0x00 , 0x82 , 0x2B , 0x9A ,
|
||||||
|
0x0F , 0x98 , 0xC2 , 0xB2 ,
|
||||||
|
0x00 , 0x34 , 0x93 , 0x0C ,
|
||||||
|
0x00 , 0x67 , 0x3D , 0x4E ,
|
||||||
|
0x0F , 0xC9 , 0x41 , 0x5A ,
|
||||||
|
0x00 , 0x02 , 0xC4 , 0x78 ,
|
||||||
|
0x00 , 0x05 , 0x88 , 0xEF ,
|
||||||
|
0x00 , 0x02 , 0xC4 , 0x78 ,
|
||||||
|
0x00 , 0xC5 , 0x84 , 0xFE ,
|
||||||
|
0xFF , 0xAF , 0x69 , 0x23 ,
|
||||||
|
0x00 , 0x65 , 0x86 , 0xF7 ,
|
||||||
|
0xFF , 0x34 , 0xF2 , 0x12 ,
|
||||||
|
0x00 , 0x65 , 0x86 , 0xF7 ,
|
||||||
|
0x00 , 0xC5 , 0x84 , 0xFE ,
|
||||||
|
0xFF , 0xAF , 0x69 , 0x23 ,
|
||||||
|
0x00 , 0xA6 , 0x66 , 0x66 ,
|
||||||
|
0x00 , 0x73 , 0x33 , 0x33 ,
|
||||||
|
0x00 , 0x6D , 0x66 , 0xD6 ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x75 , 0x69 , 0x4C ,
|
||||||
|
0x00 , 0x5A , 0x9D , 0xF8 ,
|
||||||
|
0x00 , 0x45 , 0xEF , 0xFF ,
|
||||||
|
0x00 , 0x35 , 0xFA , 0x27 ,
|
||||||
|
0x00 , 0x29 , 0xA8 , 0xBB ,
|
||||||
|
0x00 , 0x20 , 0x26 , 0xF3 ,
|
||||||
|
0x00 , 0x06 , 0x4A , 0x36 ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0x6C ,
|
||||||
|
0x00 , 0x00 , 0x5D , 0xC0 ,
|
||||||
|
0x00 , 0x08 , 0x0D , 0x6A ,
|
||||||
|
0x00 , 0x02 , 0x78 , 0x8C ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x00 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x00 , 0x00 , 0x00 ,
|
||||||
@@ -42,6 +146,52 @@
|
|||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x00 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x80 , 0x00 , 0x00 ,
|
0x00 , 0x80 , 0x00 , 0x00 ,
|
||||||
|
0x00 , 0x01 , 0x01 , 0x5C ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0x64 ,
|
||||||
|
0x00 , 0x00 , 0x02 , 0xC8 ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0x64 ,
|
||||||
|
0x00 , 0xFD , 0xA1 , 0x53 ,
|
||||||
|
0x0F , 0x82 , 0x59 , 0x1D ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x28 , 0xDA , 0x08 ,
|
||||||
|
0x00 , 0x29 , 0x53 , 0x21 ,
|
||||||
|
0x00 , 0x2A , 0xC6 , 0xE7 ,
|
||||||
|
0x00 , 0x2C , 0x47 , 0xBE ,
|
||||||
|
0x00 , 0x2D , 0xD6 , 0x1B ,
|
||||||
|
0x00 , 0x2F , 0x72 , 0x77 ,
|
||||||
|
0x00 , 0x31 , 0x1D , 0x52 ,
|
||||||
|
0x00 , 0x32 , 0xD7 , 0x2C ,
|
||||||
|
0x00 , 0x34 , 0xA0 , 0x8E ,
|
||||||
|
0x00 , 0x36 , 0x7A , 0x02 ,
|
||||||
|
0x00 , 0x00 , 0x06 , 0xD3 ,
|
||||||
|
0x00 , 0x00 , 0x4E , 0xB0 ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0xE0 ,
|
||||||
|
0x00 , 0x00 , 0x07 , 0x1C ,
|
||||||
|
0x00 , 0x00 , 0x4E , 0xB0 ,
|
||||||
|
0x00 , 0x00 , 0x01 , 0xE0 ,
|
||||||
|
0x00 , 0x00 , 0x07 , 0x1C ,
|
||||||
0x0F , 0xFE , 0x2B , 0xE3 ,
|
0x0F , 0xFE , 0x2B , 0xE3 ,
|
||||||
0x00 , 0x22 , 0xBE , 0x2C ,
|
0x00 , 0x22 , 0xBE , 0x2C ,
|
||||||
0x0F , 0xEA , 0xAA , 0xAB ,
|
0x0F , 0xEA , 0xAA , 0xAB ,
|
||||||
@@ -872,154 +1022,4 @@
|
|||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x00 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x00 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
0x00 , 0x00 , 0x00 , 0x00 ,
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
0x00 , 0x00 , 0x00 , 0x00 ,
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* File: C:\Users\mikbi\Documents\Allegati\Desktop\DigiRadio\DigiRadioModificato\DigiRadio_IC_1_REG.h
|
* File: C:\Users\mikbi\Documents\Allegati\Desktop\Sigmastudio\DigiRadioFinale_IC_1_REG.h
|
||||||
*
|
*
|
||||||
* Created: Friday, August 7, 2026 3:19:11 AM
|
* Created: Tuesday, August 25, 2026 8:11:05 PM
|
||||||
* Description: DigiRadio:IC 1 control register definitions.
|
* Description: DigiRadioFinale:IC 1 control register definitions.
|
||||||
*
|
*
|
||||||
* This software is distributed in the hope that it will be useful,
|
* This software is distributed in the hope that it will be useful,
|
||||||
* but is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
* but is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
*
|
*
|
||||||
* Copyright ©2026 Analog Devices, Inc. All rights reserved.
|
* Copyright ©2026 Analog Devices, Inc. All rights reserved.
|
||||||
*/
|
*/
|
||||||
#ifndef __DIGIRADIO_IC_1_REG_H__
|
#ifndef __DIGIRADIOFINALE_IC_1_REG_H__
|
||||||
#define __DIGIRADIO_IC_1_REG_H__
|
#define __DIGIRADIOFINALE_IC_1_REG_H__
|
||||||
|
|
||||||
|
|
||||||
/* InterfaceRegister0 - Registers (IC 1) */
|
/* InterfaceRegister0 - Registers (IC 1) */
|
||||||
|
|||||||
@@ -17,77 +17,125 @@
|
|||||||
<NetList>
|
<NetList>
|
||||||
<IC name=" IC 1 " type="DSPSigma100">
|
<IC name=" IC 1 " type="DSPSigma100">
|
||||||
<Schematic>
|
<Schematic>
|
||||||
<Algorithm name="ICSigma100In1" friendlyname="170x\140x input " cell="Input1 " location="{X=-15, Y=104} " Growth="10 " GrowthB="0 " FS="48000">
|
<Algorithm name="ICSigma100In1" friendlyname="170x\140x input " cell="Input1 " location="{X=-71, Y=184.3228} " Growth="10 " GrowthB="0 " FS="48000">
|
||||||
<Link pin="O_C33_A0_P3_out" dir="out" link="Link0" />
|
<Link pin="O_C33_A0_P3_out" dir="out" link="Link4" />
|
||||||
<Link pin="O_C33_A0_P4_out" dir="out" link="Link1" />
|
<Link pin="O_C33_A0_P4_out" dir="out" link="Link5" />
|
||||||
<Link pin="O_C33_A0_P5_out" dir="out" link="Link2" />
|
<Link pin="O_C33_A0_P5_out" dir="out" link="Link6" />
|
||||||
<Link pin="O_C33_A0_P6_out" dir="out" link="Link3" />
|
<Link pin="O_C33_A0_P6_out" dir="out" link="Link7" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="BeepAlg1" friendlyname="Beep - variable gain " cell="Beep1 " location="{X=30, Y=459} " Growth="0 " GrowthB="0 " FS="48000">
|
<Algorithm name="BeepAlg1" friendlyname="Beep - variable gain " cell="Beep1 " location="{X=136, Y=723.2243} " Growth="0 " GrowthB="0 " FS="48000">
|
||||||
<Link pin="O_C0_A0_P1_out" dir="out" link="Link15" />
|
<Link pin="O_C0_A0_P1_out" dir="out" link="Link16" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="Gain1940AlgNS1" friendlyname="Gain (no slew) " cell="Si4674 " location="{X=168, Y=5} " Growth="0 " GrowthB="0">
|
<Algorithm name="DCInpAlg1" friendlyname="dc Input " cell="DC1 " location="{X=609, Y=89.22221} " Growth="0 " GrowthB="0 " FS="48000">
|
||||||
<Link pin="I_C63_A0_P1_in" dir="in" link="Link0" />
|
<Link pin="O_C216_A0_P1_out" dir="out" link="Link19" />
|
||||||
<Link pin="O_C63_A0_P2_out" dir="out" link="Link4" />
|
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="Gain1940AlgNS2" friendlyname="Gain (no slew) " cell="Si4674 " location="{X=168, Y=5} " Growth="0 " GrowthB="0">
|
<Algorithm name="DCBlockAlgDoublePrec1" friendlyname="DC Blocking " cell="DCB1 " location="{X=101, Y=61.38417} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C63_A1_P1_in" dir="in" link="Link1" />
|
<Link pin="I_C67_A0_P1_in" dir="in" link="Link4" />
|
||||||
<Link pin="O_C63_A1_P2_out" dir="out" link="Link5" />
|
<Link pin="O_C67_A0_P2_out" dir="out" link="Link2" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="Gain1940AlgNS3" friendlyname="Gain (no slew) " cell="ESP32 " location="{X=166, Y=196} " Growth="0 " GrowthB="0">
|
<Algorithm name="DCBlockAlgDoublePrec2" friendlyname="DC Blocking " cell="DCB2 " location="{X=104, Y=138.3348} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C75_A0_P1_in" dir="in" link="Link2" />
|
<Link pin="I_C72_A0_P1_in" dir="in" link="Link5" />
|
||||||
<Link pin="O_C75_A0_P2_out" dir="out" link="Link6" />
|
<Link pin="O_C72_A0_P2_out" dir="out" link="Link3" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="Gain1940AlgNS4" friendlyname="Gain (no slew) " cell="ESP32 " location="{X=166, Y=196} " Growth="0 " GrowthB="0">
|
<Algorithm name="DCBlockAlgDoublePrec4" friendlyname="DC Blocking " cell="DCB4 " location="{X=103, Y=323.756} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C75_A1_P1_in" dir="in" link="Link3" />
|
<Link pin="I_C91_A0_P1_in" dir="in" link="Link6" />
|
||||||
<Link pin="O_C75_A1_P2_out" dir="out" link="Link7" />
|
<Link pin="O_C91_A0_P2_out" dir="out" link="Link10" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="Gain1940AlgNS7" friendlyname="Gain (no slew) " cell="Single 1 " location="{X=188, Y=404} " Growth="0 " GrowthB="0">
|
<Algorithm name="DCBlockAlgDoublePrec3" friendlyname="DC Blocking " cell="DCB3 " location="{X=92, Y=505.472} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C4_A0_P1_in" dir="in" link="Link15" />
|
<Link pin="I_C81_A0_P1_in" dir="in" link="Link7" />
|
||||||
<Link pin="O_C4_A0_P2_out" dir="out" link="Link16" />
|
<Link pin="O_C81_A0_P2_out" dir="out" link="Link11" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="SingleCtrlSplit1" friendlyname="Splitter Single " cell="S Splitter1 " location="{X=289, Y=440} " Growth="2 " GrowthB="0">
|
<Algorithm name="SingleCtrlSplit1" friendlyname="Splitter Single " cell="S Splitter1 " location="{X=332, Y=704.3125} " Growth="2 " GrowthB="0">
|
||||||
<Link pin="I_C9_A0_P1_in" dir="in" link="Link16" />
|
<Link pin="I_C9_A0_P1_in" dir="in" link="Link16" />
|
||||||
<Link pin="O_C9_A0_P2_out" dir="out" link="Link17" />
|
<Link pin="O_C9_A0_P2_out" dir="out" link="Link17" />
|
||||||
<Link pin="O_C9_A0_P3_out" dir="out" link="Link18" />
|
<Link pin="O_C9_A0_P3_out" dir="out" link="Link18" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="StereoMixerAlg1" friendlyname="Stereo Mixer " cell="St Mixer1 " location="{X=464, Y=112} " Growth="3 " GrowthB="0">
|
<Algorithm name="TwoChannelSingleDetectAlgFix1" friendlyname="Stereo RMS (Post Gain) " cell="Compressor1 " location="{X=187, Y=-4.364594} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C84_A0_P3_in" dir="in" link="Link4" />
|
<Link pin="I_C106_A0_P1_in" dir="in" link="Link2" />
|
||||||
<Link pin="I_C84_A0_P4_in" dir="in" link="Link5" />
|
<Link pin="I_C106_A0_P2_in" dir="in" link="Link3" />
|
||||||
<Link pin="I_C84_A0_P5_in" dir="in" link="Link6" />
|
<Link pin="O_C106_A0_P3_out" dir="out" link="Link8" />
|
||||||
<Link pin="I_C84_A0_P6_in" dir="in" link="Link7" />
|
<Link pin="O_C106_A0_P4_out" dir="out" link="Link9" />
|
||||||
<Link pin="I_C84_A0_P7_in" dir="in" link="Link17" />
|
|
||||||
<Link pin="I_C84_A0_P8_in" dir="in" link="Link18" />
|
|
||||||
<Link pin="O_C84_A0_P1_out" dir="out" link="Link8" />
|
|
||||||
<Link pin="O_C84_A0_P2_out" dir="out" link="Link9" />
|
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="PEQ1Chan1" friendlyname="PEQ1Chan - Double Precision " cell="Param EQ1 " location="{X=585, Y=153} " Growth="2 " GrowthB="0">
|
<Algorithm name="SingleBandLevelDet3" friendlyname="Single Band " cell="1 X RTA3 " location="{X=352, Y=296} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C125_A0_P1_in" dir="in" link="Link8" />
|
<Link pin="I_C155_A0_P1_in" dir="in" link="Link10" />
|
||||||
<Link pin="I_C125_A0_P3_in" dir="in" link="Link9" />
|
<Link pin="O_C155_A0_P2_out" dir="out" link="Link14" />
|
||||||
<Link pin="O_C125_A0_P2_out" dir="out" link="Link10" />
|
|
||||||
<Link pin="O_C125_A0_P4_out" dir="out" link="Link11" />
|
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="Gain1940AlgNS5" friendlyname="Gain (no slew) " cell="Multiple 1 " location="{X=711, Y=85} " Growth="0 " GrowthB="0">
|
<Algorithm name="SingleBandLevelDet4" friendlyname="Single Band " cell="1 X RTA4 " location="{X=355, Y=469} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C138_A0_P1_in" dir="in" link="Link10" />
|
<Link pin="I_C158_A0_P1_in" dir="in" link="Link11" />
|
||||||
<Link pin="O_C138_A0_P2_out" dir="out" link="Link12" />
|
<Link pin="O_C158_A0_P2_out" dir="out" link="Link15" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="Gain1940AlgNS6" friendlyname="Gain (no slew) " cell="Multiple 1 " location="{X=711, Y=85} " Growth="0 " GrowthB="0">
|
<Algorithm name="SingleBandLevelDet1" friendlyname="Single Band " cell="1 X RTA1 " location="{X=350, Y=-73} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C138_A1_P1_in" dir="in" link="Link11" />
|
<Link pin="I_C149_A0_P1_in" dir="in" link="Link8" />
|
||||||
<Link pin="O_C138_A1_P2_out" dir="out" link="Link13" />
|
<Link pin="O_C149_A0_P2_out" dir="out" link="Link12" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="limiterAlg1" friendlyname="Limiter with Indicator " cell="Limiter 1 " location="{X=844, Y=37} " Growth="0 " GrowthB="0">
|
<Algorithm name="SingleBandLevelDet2" friendlyname="Single Band " cell="1 X RTA2 " location="{X=344, Y=112} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C169_A0_P1_in" dir="in" link="Link12" />
|
<Link pin="I_C152_A0_P1_in" dir="in" link="Link9" />
|
||||||
<Link pin="O_C169_A0_P2_out" dir="out" link="Link19" />
|
<Link pin="O_C152_A0_P2_out" dir="out" link="Link13" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="limiterAlg2" friendlyname="Limiter with Indicator " cell="Limiter 2 " location="{X=848, Y=186} " Growth="0 " GrowthB="0">
|
<Algorithm name="DCinputmux_stereo_Alg1" friendlyname="Stereo " cell="MX1 " location="{X=730, Y=222.1111} " Growth="3 " GrowthB="0">
|
||||||
<Link pin="I_C174_A0_P1_in" dir="in" link="Link13" />
|
<Link pin="I_C174_A0_P3_in" dir="in" link="Link19" />
|
||||||
<Link pin="O_C174_A0_P2_out" dir="out" link="Link14" />
|
<Link pin="I_C174_A0_P4_in" dir="in" link="Link12" />
|
||||||
|
<Link pin="I_C174_A0_P5_in" dir="in" link="Link13" />
|
||||||
|
<Link pin="I_C174_A0_P6_in" dir="in" link="Link14" />
|
||||||
|
<Link pin="I_C174_A0_P7_in" dir="in" link="Link15" />
|
||||||
|
<Link pin="I_C174_A0_P8_in" dir="in" link="Link17" />
|
||||||
|
<Link pin="I_C174_A0_P9_in" dir="in" link="Link18" />
|
||||||
|
<Link pin="O_C174_A0_P1_out" dir="out" link="Link20" />
|
||||||
|
<Link pin="O_C174_A0_P2_out" dir="out" link="Link21" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="ICSigma100Out2" friendlyname="170x\140x output " cell="Output2 " location="{X=1003, Y=202} " Growth="0 " GrowthB="0">
|
<Algorithm name="PEQ1Chan1" friendlyname="PEQ1Chan - Double Precision " cell="Param EQ1 " location="{X=820, Y=211.9035} " Growth="2 " GrowthB="0">
|
||||||
<Link pin="I_C185_A0_P1_in" dir="in" link="Link14" />
|
<Link pin="I_C125_A0_P1_in" dir="in" link="Link20" />
|
||||||
|
<Link pin="I_C125_A0_P3_in" dir="in" link="Link21" />
|
||||||
|
<Link pin="O_C125_A0_P2_out" dir="out" link="Link22" />
|
||||||
|
<Link pin="O_C125_A0_P4_out" dir="out" link="Link23" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
<Algorithm name="ICSigma100Out1" friendlyname="170x\140x output " cell="Output1 " location="{X=1009.037, Y=52} " Growth="0 " GrowthB="0">
|
<Algorithm name="SuperPhatAlg1" friendlyname="Super Bass " cell="SPhat1 " location="{X=921, Y=196} " Growth="0 " GrowthB="0">
|
||||||
<Link pin="I_C183_A0_P1_in" dir="in" link="Link19" />
|
<Link pin="I_C227_A0_P1_in" dir="in" link="Link22" />
|
||||||
|
<Link pin="I_C227_A0_P2_in" dir="in" link="Link23" />
|
||||||
|
<Link pin="O_C227_A0_P3_out" dir="out" link="Link24" />
|
||||||
|
<Link pin="O_C227_A0_P4_out" dir="out" link="Link25" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="EQ1940DualS1" friendlyname="2 Channel - Single Precision " cell="Gen Filter1 " location="{X=1037, Y=197} " Growth="1 " GrowthB="0">
|
||||||
|
<Link pin="I_C232_A0_P1_in" dir="in" link="Link24" />
|
||||||
|
<Link pin="I_C232_A0_P2_in" dir="in" link="Link25" />
|
||||||
|
<Link pin="O_C232_A0_P3_out" dir="out" link="Link26" />
|
||||||
|
<Link pin="O_C232_A0_P4_out" dir="out" link="Link27" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="Gain1940AlgNS5" friendlyname="Gain (no slew) " cell="Multiple 1 " location="{X=1214, Y=149.7451} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C138_A0_P1_in" dir="in" link="Link26" />
|
||||||
|
<Link pin="O_C138_A0_P2_out" dir="out" link="Link33" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="Gain1940AlgNS6" friendlyname="Gain (no slew) " cell="Multiple 1 " location="{X=1214, Y=149.7451} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C138_A1_P1_in" dir="in" link="Link27" />
|
||||||
|
<Link pin="O_C138_A1_P2_out" dir="out" link="Link32" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="BassBAlg_stereo1" friendlyname="Stereo Dynamic Bass Boost " cell="Bass Boost1 " location="{X=1394, Y=131.4268} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C263_A0_P1_in" dir="in" link="Link33" />
|
||||||
|
<Link pin="I_C263_A0_P2_in" dir="in" link="Link32" />
|
||||||
|
<Link pin="O_C263_A0_P3_out" dir="out" link="Link31" />
|
||||||
|
<Link pin="O_C263_A0_P4_out" dir="out" link="Link30" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="SingleBandLevelDet6" friendlyname="Single Band " cell="1 X RTA2_2 " location="{X=1676, Y=243} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C252_A0_P1_in" dir="in" link="Link30" />
|
||||||
|
<Link pin="O_C252_A0_P2_out" dir="out" link="Link28" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="SingleBandLevelDet5" friendlyname="Single Band " cell="1 X RTA1_2 " location="{X=1676, Y=68} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C249_A0_P1_in" dir="in" link="Link31" />
|
||||||
|
<Link pin="O_C249_A0_P2_out" dir="out" link="Link29" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="limiterAlg2" friendlyname="Limiter with Indicator " cell="Limiter 2 " location="{X=1800, Y=269.5105} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C29_A0_P1_in" dir="in" link="Link28" />
|
||||||
|
<Link pin="O_C29_A0_P2_out" dir="out" link="Link1" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="limiterAlg1" friendlyname="Limiter with Indicator " cell="Limiter 1 " location="{X=1803, Y=90.51053} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C169_A0_P1_in" dir="in" link="Link29" />
|
||||||
|
<Link pin="O_C169_A0_P2_out" dir="out" link="Link0" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="ICSigma100Out1" friendlyname="170x\140x output " cell="Output1 " location="{X=1958, Y=107.0044} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C183_A0_P1_in" dir="in" link="Link0" />
|
||||||
|
</Algorithm>
|
||||||
|
<Algorithm name="ICSigma100Out2" friendlyname="170x\140x output " cell="Output2 " location="{X=1955, Y=285.5105} " Growth="0 " GrowthB="0">
|
||||||
|
<Link pin="I_C48_A0_P1_in" dir="in" link="Link1" />
|
||||||
</Algorithm>
|
</Algorithm>
|
||||||
</Schematic>
|
</Schematic>
|
||||||
</IC>
|
</IC>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* File: defines.h
|
* File: defines.h
|
||||||
*
|
*
|
||||||
* Created: Friday, August 7, 2026 3:19:11 AM
|
* Created: Tuesday, August 25, 2026 8:11:05 PM
|
||||||
* Description: DigiRadio IC default download data definitions.
|
* Description: DigiRadioFinale IC default download data definitions.
|
||||||
*
|
*
|
||||||
* This software is distributed in the hope that it will be useful,
|
* This software is distributed in the hope that it will be useful,
|
||||||
* but is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
* but is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
@@ -19,11 +19,9 @@ idf_component_register(
|
|||||||
"src/EqBandIndex.cpp"
|
"src/EqBandIndex.cpp"
|
||||||
"src/BiquadCoefficients.cpp"
|
"src/BiquadCoefficients.cpp"
|
||||||
"src/BiquadDesign.cpp"
|
"src/BiquadDesign.cpp"
|
||||||
"src/MixerState.cpp"
|
|
||||||
"src/EqProfile.cpp"
|
"src/EqProfile.cpp"
|
||||||
"src/EnhanceLevel.cpp"
|
"src/EnhanceLevel.cpp"
|
||||||
"src/AudioEnhancements.cpp"
|
"src/AudioEnhancements.cpp"
|
||||||
"src/EnhancementsDesign.cpp"
|
|
||||||
"src/AudioProfile.cpp"
|
"src/AudioProfile.cpp"
|
||||||
"src/AudioProfileJson.cpp"
|
"src/AudioProfileJson.cpp"
|
||||||
"src/Bt1035At.cpp"
|
"src/Bt1035At.cpp"
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* @file ActiveSource.hpp
|
||||||
|
* @brief ADAU1701 MX1 input source selector.
|
||||||
|
*
|
||||||
|
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||||
|
*
|
||||||
|
* Copyright 2026 Michele Bigi
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ActiveSource — the one stereo pair MX1 currently passes through.
|
||||||
|
*
|
||||||
|
* @dname ActiveSource
|
||||||
|
* @return n/a (type)
|
||||||
|
* @pubstate n/a
|
||||||
|
*
|
||||||
|
* The DigiRadioFinale SigmaStudio revision (2026-08-25) replaced St Mixer1
|
||||||
|
* (which combined Si4684 + ESP32 + Beep simultaneously, each at its own
|
||||||
|
* gain) with MX1, a DC-controlled exclusive mux: only one of the three
|
||||||
|
* stereo pairs reaches Param EQ1 at a time, selected by the DC1 cell.
|
||||||
|
* Per-source gain trims (the old Si4674/ESP32 cells) no longer exist —
|
||||||
|
* level is controlled solely by Master Volume and the EQ, downstream of
|
||||||
|
* the selected source.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
enum class ActiveSource {
|
||||||
|
Radio, ///< Si4684 tuner (FM/DAB), MX1 pair index 0.
|
||||||
|
Bluetooth, ///< ESP32 I2S input, MX1 pair index 1.
|
||||||
|
Beep, ///< Internal test tone, MX1 pair index 2.
|
||||||
|
};
|
||||||
|
|
||||||
|
// DC1 is a raw 32-bit integer index (0/1/2), not the 5.23 fixpoint its
|
||||||
|
// compiled TYPE_DC1/VALUE_DC1 macros would suggest -- confirmed live
|
||||||
|
// 2026-08-25 (see adau1701::paramSourceIndex() and Adau1701Driver::
|
||||||
|
// selectSource()).
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
@@ -17,19 +17,20 @@
|
|||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief AudioEnhancements — psychoacoustic EQ overlays (0–100 each).
|
* @brief AudioEnhancements — dedicated-block enhancement levels (0–100).
|
||||||
*
|
*
|
||||||
* @dname AudioEnhancements
|
* @dname AudioEnhancements
|
||||||
* @return n/a (type)
|
* @return n/a (type)
|
||||||
* @pubstate Mapped to PEQ bands at runtime (manual chapter sigmastudio).
|
* @pubstate Applied via IDsp::setStereoSpreadLevel/setBassBoostLevel, which
|
||||||
* No dedicated SigmaStudio blocks; see \c applyEnhancementsToEq.
|
* scale the SPhat1/Bass Boost1 SigmaStudio blocks toward their
|
||||||
|
* compiled response; does not touch EqProfile.
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
* @date 2026-07-06
|
* @date 2026-07-06
|
||||||
*/
|
*/
|
||||||
struct AudioEnhancements {
|
struct AudioEnhancements {
|
||||||
EnhanceLevel stereo; ///< Stereo depth / presence (PEQ bands 3–5).
|
EnhanceLevel stereo; ///< Stereo spread (SPhat1 spatializer).
|
||||||
EnhanceLevel bass; ///< Bass emphasis (PEQ bands 1–2).
|
EnhanceLevel bass; ///< Bass boost intensity (Bass Boost1).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief factoryDefault — both enhancements off.
|
* @brief factoryDefault — both enhancements off.
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* @file AudioLevels.hpp
|
||||||
|
* @brief Snapshot of the six ADAU1701 Data Capture level meters.
|
||||||
|
*
|
||||||
|
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
||||||
|
*
|
||||||
|
* Copyright 2026 Michele Bigi
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief AudioLevels — one on-demand read of all six 1×RTA meters.
|
||||||
|
*
|
||||||
|
* @dname AudioLevels
|
||||||
|
* @return n/a (type)
|
||||||
|
* @pubstate Read-only snapshot; not part of AudioProfile, never persisted.
|
||||||
|
*
|
||||||
|
* Each field is in dBFS, read live from the ADAU1701 Data Capture Register
|
||||||
|
* (address 2074, MAC_out tap) at the program-step index SigmaStudio's
|
||||||
|
* compiler assigned to that 1×RTA cell (see
|
||||||
|
* Firmware/ADAU1701-Firmware/IC 1_DigiRadioFinale/net_list_out2/trap.dat).
|
||||||
|
* Captured only when requested by an API call -- no background polling.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
struct AudioLevels {
|
||||||
|
float radioInLeftDb; ///< 1×RTA1, post-compressor Si4684 L.
|
||||||
|
float radioInRightDb; ///< 1×RTA2, post-compressor Si4684 R.
|
||||||
|
float bluetoothInLeftDb; ///< 1×RTA3, ESP32 L.
|
||||||
|
float bluetoothInRightDb; ///< 1×RTA4, ESP32 R.
|
||||||
|
float outputLeftDb; ///< 1×RTA1_2, post Bass Boost1, L.
|
||||||
|
float outputRightDb; ///< 1×RTA2_2, post Bass Boost1, R.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
@@ -12,10 +12,10 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/ActiveSource.hpp"
|
||||||
#include "core/AudioEnhancements.hpp"
|
#include "core/AudioEnhancements.hpp"
|
||||||
#include "core/EqProfile.hpp"
|
#include "core/EqProfile.hpp"
|
||||||
#include "core/GainDb.hpp"
|
#include "core/GainDb.hpp"
|
||||||
#include "core/MixerState.hpp"
|
|
||||||
|
|
||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ namespace core {
|
|||||||
* @date 2026-07-06
|
* @date 2026-07-06
|
||||||
*/
|
*/
|
||||||
struct AudioProfile {
|
struct AudioProfile {
|
||||||
MixerState mixer; ///< Input and stereo-mixer gains.
|
ActiveSource activeSource; ///< Which MX1 pair reaches Param EQ1.
|
||||||
EqProfile eq; ///< Six-band parametric EQ.
|
EqProfile eq; ///< Six-band parametric EQ.
|
||||||
GainDb masterLeft; ///< Multiple 1 master volume, left.
|
GainDb masterLeft; ///< Multiple 1 master volume, left.
|
||||||
GainDb masterRight; ///< Multiple 1 master volume, right.
|
GainDb masterRight; ///< Multiple 1 master volume, right.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/AudioLevels.hpp"
|
||||||
#include "core/AudioProfile.hpp"
|
#include "core/AudioProfile.hpp"
|
||||||
#include "core/ParseError.hpp"
|
#include "core/ParseError.hpp"
|
||||||
|
|
||||||
@@ -104,4 +105,17 @@ namespace core {
|
|||||||
[[nodiscard]] std::expected<bool, ParseError> parseBeepEnabledJson(
|
[[nodiscard]] std::expected<bool, ParseError> parseBeepEnabledJson(
|
||||||
std::string_view json);
|
std::string_view json);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief serializeAudioLevelsJson — serialise a level-meter snapshot.
|
||||||
|
*
|
||||||
|
* @dname serializeAudioLevelsJson
|
||||||
|
* @param levels On-demand read from IDsp::readLevels().
|
||||||
|
* @return JSON object string for GET /api/audio/levels.
|
||||||
|
* @pubstate none
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::string serializeAudioLevelsJson(const AudioLevels& levels);
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file EnhancementsDesign.hpp
|
|
||||||
* @brief Map enhancement levels onto Param EQ1 bands (host-testable).
|
|
||||||
*
|
|
||||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
|
||||||
*
|
|
||||||
* Copyright 2026 Michele Bigi
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/AudioEnhancements.hpp"
|
|
||||||
#include "core/EqProfile.hpp"
|
|
||||||
|
|
||||||
namespace core {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief applyEnhancementsToEq — merge enhancement overlays into an EQ profile.
|
|
||||||
*
|
|
||||||
* @dname applyEnhancementsToEq
|
|
||||||
* @param base User EQ settings (bands not touched stay unchanged).
|
|
||||||
* @param enhancements Stereo and bass levels (0 = use base band only).
|
|
||||||
* @return EqProfile with affected PEQ bands updated for safeload.
|
|
||||||
* @pubstate none
|
|
||||||
*
|
|
||||||
* Stereo (bands 3–5): slight 1\,kHz cut plus 3/8\,kHz lift for depth.
|
|
||||||
* Bass (bands 1–2): 100\,Hz and 400\,Hz peaking boost.
|
|
||||||
* Band 0 (high-pass) is never modified.
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
[[nodiscard]] EqProfile applyEnhancementsToEq(
|
|
||||||
const EqProfile& base, const AudioEnhancements& enhancements) noexcept;
|
|
||||||
|
|
||||||
} // namespace core
|
|
||||||
@@ -12,13 +12,14 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/ActiveSource.hpp"
|
||||||
|
#include "core/AudioLevels.hpp"
|
||||||
#include "core/AudioProfile.hpp"
|
#include "core/AudioProfile.hpp"
|
||||||
#include "core/DspError.hpp"
|
#include "core/DspError.hpp"
|
||||||
|
#include "core/EnhanceLevel.hpp"
|
||||||
#include "core/EqBandIndex.hpp"
|
#include "core/EqBandIndex.hpp"
|
||||||
#include "core/FrequencyHz.hpp"
|
#include "core/FrequencyHz.hpp"
|
||||||
#include "core/GainDb.hpp"
|
#include "core/GainDb.hpp"
|
||||||
#include "core/MixSource.hpp"
|
|
||||||
#include "core/MixerState.hpp"
|
|
||||||
#include "core/EqProfile.hpp"
|
#include "core/EqProfile.hpp"
|
||||||
|
|
||||||
#include <expected>
|
#include <expected>
|
||||||
@@ -56,18 +57,18 @@ public:
|
|||||||
const AudioProfile& profile) = 0;
|
const AudioProfile& profile) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief applyMixer — safeload input and stereo-mixer gains.
|
* @brief selectSource — safeload MX1's DC1 control to pick one source.
|
||||||
*
|
*
|
||||||
* @dname applyMixer
|
* @dname selectSource
|
||||||
* @param mixer Per-source and St Mixer1 levels.
|
* @param source Which stereo pair MX1 passes through to Param EQ1.
|
||||||
* @return Ok on success, or DspError.
|
* @return Ok on success, or DspError.
|
||||||
* @pubstate writes ADAU1701 parameter RAM via safeload.
|
* @pubstate writes ADAU1701 parameter RAM via safeload.
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
* @date 2026-07-06
|
* @date 2026-08-25
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] virtual std::expected<void, DspError> applyMixer(
|
[[nodiscard]] virtual std::expected<void, DspError> selectSource(
|
||||||
const MixerState& mixer) = 0;
|
ActiveSource source) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief applyEq — safeload all six PEQ bands.
|
* @brief applyEq — safeload all six PEQ bands.
|
||||||
@@ -83,22 +84,6 @@ public:
|
|||||||
[[nodiscard]] virtual std::expected<void, DspError> applyEq(
|
[[nodiscard]] virtual std::expected<void, DspError> applyEq(
|
||||||
const EqProfile& eq) = 0;
|
const EqProfile& eq) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief setInputVolume — safeload one input path (Si4684 or ESP32).
|
|
||||||
*
|
|
||||||
* @dname setInputVolume
|
|
||||||
* @param source Tuner or ESP32 I2S path.
|
|
||||||
* @param left Left channel gain.
|
|
||||||
* @param right Right channel gain.
|
|
||||||
* @return Ok on success, or DspError.
|
|
||||||
* @pubstate writes ADAU1701 parameter RAM via safeload.
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
[[nodiscard]] virtual std::expected<void, DspError> setInputVolume(
|
|
||||||
MixSource source, GainDb left, GainDb right) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief setMasterVolume — safeload Multiple 1 master output gain.
|
* @brief setMasterVolume — safeload Multiple 1 master output gain.
|
||||||
*
|
*
|
||||||
@@ -146,6 +131,42 @@ public:
|
|||||||
[[nodiscard]] virtual std::expected<void, DspError> setBeepEnabled(
|
[[nodiscard]] virtual std::expected<void, DspError> setBeepEnabled(
|
||||||
bool enabled) = 0;
|
bool enabled) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief setBassBoostLevel — scale Bass Boost1 toward its compiled
|
||||||
|
* curve.
|
||||||
|
*
|
||||||
|
* @dname setBassBoostLevel
|
||||||
|
* @param level 0 = flat bypass, 100 = the full SigmaStudio-tuned
|
||||||
|
* Dynamic Bass Boost response.
|
||||||
|
* @return Ok on success, or DspError.
|
||||||
|
* @pubstate writes ADAU1701 parameter RAM via safeload. Linearly
|
||||||
|
* interpolates the compiled crossover filter and 33-point
|
||||||
|
* compander curve toward identity/unity; BASSFREQUENCY and
|
||||||
|
* the time constant are left at their tuned defaults.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual std::expected<void, DspError> setBassBoostLevel(
|
||||||
|
EnhanceLevel level) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief setStereoSpreadLevel — scale SPhat1's stereo spread amount.
|
||||||
|
*
|
||||||
|
* @dname setStereoSpreadLevel
|
||||||
|
* @param level 0 = no added spread, 100 = the full SigmaStudio-tuned
|
||||||
|
* SuperPhat spread.
|
||||||
|
* @return Ok on success, or DspError.
|
||||||
|
* @pubstate writes ADAU1701 parameter RAM via safeload. Scales
|
||||||
|
* SPREAD1/SPREAD2 linearly; the crossover filter and
|
||||||
|
* compander curve are left at their tuned defaults.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual std::expected<void, DspError> setStereoSpreadLevel(
|
||||||
|
EnhanceLevel level) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief writeRawParam — safeload any named Parameter RAM cell.
|
* @brief writeRawParam — safeload any named Parameter RAM cell.
|
||||||
*
|
*
|
||||||
@@ -163,6 +184,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] virtual std::expected<void, DspError> writeRawParam(
|
[[nodiscard]] virtual std::expected<void, DspError> writeRawParam(
|
||||||
unsigned address, float value) = 0;
|
unsigned address, float value) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief readLevels — on-demand read of all six 1×RTA level meters.
|
||||||
|
*
|
||||||
|
* @dname readLevels
|
||||||
|
* @return AudioLevels snapshot, or DspError.
|
||||||
|
* @pubstate Reads live from the ADAU1701; not part of AudioProfile,
|
||||||
|
* never persisted. Only runs when called, no background
|
||||||
|
* polling.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
[[nodiscard]] virtual std::expected<AudioLevels, DspError> readLevels() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file MixSource.hpp
|
|
||||||
* @brief ADAU1701 input path selector for per-source volume control.
|
|
||||||
*
|
|
||||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
|
||||||
*
|
|
||||||
* Copyright 2026 Michele Bigi
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace core {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief MixSource — Si4684 tuner or ESP32 I2S input on the ADAU1701.
|
|
||||||
*
|
|
||||||
* @dname MixSource
|
|
||||||
* @return n/a (type)
|
|
||||||
* @pubstate n/a
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
enum class MixSource {
|
|
||||||
Si4684,
|
|
||||||
Esp32,
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace core
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file MixerState.hpp
|
|
||||||
* @brief ADAU1701 input and stereo-mixer gain snapshot.
|
|
||||||
*
|
|
||||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
|
||||||
*
|
|
||||||
* Copyright 2026 Michele Bigi
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/GainDb.hpp"
|
|
||||||
|
|
||||||
namespace core {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief MixerState — per-source and St Mixer1 levels for the ADAU1701.
|
|
||||||
*
|
|
||||||
* @dname MixerState
|
|
||||||
* @return n/a (type)
|
|
||||||
* @pubstate Immutable snapshot applied by AudioService via IDsp.
|
|
||||||
*
|
|
||||||
* Maps to SigmaStudio modules Si4674, ESP32, and St Mixer1.
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
struct MixerState {
|
|
||||||
GainDb si4684Left; ///< Si4674 volume control, left channel.
|
|
||||||
GainDb si4684Right; ///< Si4674 volume control, right channel.
|
|
||||||
GainDb esp32Left; ///< ESP32 volume control, left channel.
|
|
||||||
GainDb esp32Right; ///< ESP32 volume control, right channel.
|
|
||||||
GainDb mixLeft; ///< St Mixer1 ST0 (Si4684) input level.
|
|
||||||
GainDb mixRight; ///< St Mixer1 ST1 (ESP32) input level.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief factoryDefault — unity gains on all paths.
|
|
||||||
*
|
|
||||||
* @dname factoryDefault
|
|
||||||
* @return MixerState with 0 dB on every control.
|
|
||||||
* @pubstate none
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
[[nodiscard]] static MixerState factoryDefault() noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief radioFirst — Si4684 path open, ESP32 path muted.
|
|
||||||
*
|
|
||||||
* @dname radioFirst
|
|
||||||
* @return MixerState for FM/DAB tuner audio to the Bose I2S chain.
|
|
||||||
* @pubstate none
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-08-05
|
|
||||||
*/
|
|
||||||
[[nodiscard]] static MixerState radioFirst() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace core
|
|
||||||
@@ -19,7 +19,7 @@ AudioProfile AudioProfile::factoryDefault() noexcept
|
|||||||
{
|
{
|
||||||
const GainDb unity = GainDb::zero();
|
const GainDb unity = GainDb::zero();
|
||||||
return AudioProfile{
|
return AudioProfile{
|
||||||
.mixer = MixerState::factoryDefault(),
|
.activeSource = ActiveSource::Radio,
|
||||||
.eq = EqProfile::factoryDefault(),
|
.eq = EqProfile::factoryDefault(),
|
||||||
.masterLeft = unity,
|
.masterLeft = unity,
|
||||||
.masterRight = unity,
|
.masterRight = unity,
|
||||||
|
|||||||
@@ -92,35 +92,48 @@ namespace {
|
|||||||
return GainDb::tryFromDb(db);
|
return GainDb::tryFromDb(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<MixerState, ParseError> parseMixerJson(
|
[[nodiscard]] std::string_view activeSourceName(ActiveSource source) noexcept
|
||||||
|
{
|
||||||
|
switch (source) {
|
||||||
|
case ActiveSource::Radio:
|
||||||
|
return "radio";
|
||||||
|
case ActiveSource::Bluetooth:
|
||||||
|
return "bluetooth";
|
||||||
|
case ActiveSource::Beep:
|
||||||
|
return "beep";
|
||||||
|
}
|
||||||
|
return "radio";
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<ActiveSource, ParseError> parseActiveSourceJson(
|
||||||
std::string_view json)
|
std::string_view json)
|
||||||
{
|
{
|
||||||
const std::size_t mixerStart = json.find("\"mixer\"");
|
constexpr std::string_view kKey = "\"active_source\"";
|
||||||
if (mixerStart == std::string_view::npos) {
|
const std::size_t needlePos = json.find(kKey);
|
||||||
|
if (needlePos == std::string_view::npos) {
|
||||||
return std::unexpected(ParseError::MissingField);
|
return std::unexpected(ParseError::MissingField);
|
||||||
}
|
}
|
||||||
const std::string_view mixer = json.substr(mixerStart);
|
const std::size_t quoteStart = json.find('"', needlePos + kKey.size());
|
||||||
|
if (quoteStart == std::string_view::npos) {
|
||||||
const auto si4684Left = parseGainField(mixer, "si4684_left_db");
|
|
||||||
const auto si4684Right = parseGainField(mixer, "si4684_right_db");
|
|
||||||
const auto esp32Left = parseGainField(mixer, "esp32_left_db");
|
|
||||||
const auto esp32Right = parseGainField(mixer, "esp32_right_db");
|
|
||||||
const auto mixLeft = parseGainField(mixer, "mix_left_db");
|
|
||||||
const auto mixRight = parseGainField(mixer, "mix_right_db");
|
|
||||||
|
|
||||||
if (!si4684Left || !si4684Right || !esp32Left || !esp32Right || !mixLeft
|
|
||||||
|| !mixRight) {
|
|
||||||
return std::unexpected(ParseError::MissingField);
|
return std::unexpected(ParseError::MissingField);
|
||||||
}
|
}
|
||||||
|
const std::size_t quoteEnd = json.find('"', quoteStart + 1U);
|
||||||
|
if (quoteEnd == std::string_view::npos) {
|
||||||
|
return std::unexpected(ParseError::MissingField);
|
||||||
|
}
|
||||||
|
const std::string_view value =
|
||||||
|
json.substr(quoteStart + 1U, quoteEnd - quoteStart - 1U);
|
||||||
|
|
||||||
return MixerState{
|
if (value == "radio") {
|
||||||
.si4684Left = *si4684Left,
|
return ActiveSource::Radio;
|
||||||
.si4684Right = *si4684Right,
|
}
|
||||||
.esp32Left = *esp32Left,
|
if (value == "bluetooth") {
|
||||||
.esp32Right = *esp32Right,
|
return ActiveSource::Bluetooth;
|
||||||
.mixLeft = *mixLeft,
|
}
|
||||||
.mixRight = *mixRight,
|
if (value == "beep") {
|
||||||
};
|
return ActiveSource::Beep;
|
||||||
|
}
|
||||||
|
return std::unexpected(ParseError::MissingField);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<EqProfile, ParseError> parseEqJson(
|
[[nodiscard]] std::expected<EqProfile, ParseError> parseEqJson(
|
||||||
@@ -207,14 +220,8 @@ namespace {
|
|||||||
std::string serializeAudioProfileJson(const AudioProfile& profile)
|
std::string serializeAudioProfileJson(const AudioProfile& profile)
|
||||||
{
|
{
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
const auto& m = profile.mixer;
|
out << "{\"active_source\":\"" << activeSourceName(profile.activeSource)
|
||||||
out << "{\"mixer\":{"
|
<< "\","
|
||||||
<< "\"si4684_left_db\":" << m.si4684Left.value() << ','
|
|
||||||
<< "\"si4684_right_db\":" << m.si4684Right.value() << ','
|
|
||||||
<< "\"esp32_left_db\":" << m.esp32Left.value() << ','
|
|
||||||
<< "\"esp32_right_db\":" << m.esp32Right.value() << ','
|
|
||||||
<< "\"mix_left_db\":" << m.mixLeft.value() << ','
|
|
||||||
<< "\"mix_right_db\":" << m.mixRight.value() << "},"
|
|
||||||
<< "\"master\":{"
|
<< "\"master\":{"
|
||||||
<< "\"left_db\":" << profile.masterLeft.value() << ','
|
<< "\"left_db\":" << profile.masterLeft.value() << ','
|
||||||
<< "\"right_db\":" << profile.masterRight.value() << "},"
|
<< "\"right_db\":" << profile.masterRight.value() << "},"
|
||||||
@@ -223,19 +230,13 @@ std::string serializeAudioProfileJson(const AudioProfile& profile)
|
|||||||
// Per-band "locked" flag, added 2026-08-24: band 0 is a fixed high-pass
|
// Per-band "locked" flag, added 2026-08-24: band 0 is a fixed high-pass
|
||||||
// Adau1701Driver::applyEq() never safeloads (SigmaStudio band 1, ST0 in
|
// Adau1701Driver::applyEq() never safeloads (SigmaStudio band 1, ST0 in
|
||||||
// the compiled program -- whatever gain_db/center_hz/q is stored/sent
|
// the compiled program -- whatever gain_db/center_hz/q is stored/sent
|
||||||
// for it has zero audible effect, always). Bands 1-2 are overwritten by
|
// for it has zero audible effect, always). Since the 2026-08-25
|
||||||
// core::applyEnhancementsToEq() with formula-derived values whenever
|
// DigiRadioFinale revision, bass/stereo enhancement drive the dedicated
|
||||||
// bass_level > 0; bands 3-5 likewise whenever stereo_level > 0 -- a
|
// Bass Boost1/SPhat1 blocks directly instead of overwriting EQ bands,
|
||||||
// manual edit to those bands is silently inaudible while the
|
// so bands 1-5 are no longer locked by enhancement levels -- only
|
||||||
// corresponding enhancement is active. This was previously
|
// band 0 remains permanently inert.
|
||||||
// undiscoverable from the API response (GET echoed back the stored,
|
|
||||||
// inert value with no indication it wasn't what was actually playing);
|
|
||||||
// "locked":true now tells a client to grey out that slider instead of
|
|
||||||
// letting the user "fix" a value that can't take effect.
|
|
||||||
const bool bassLocks = profile.enhancements.bass.value() > 0U;
|
|
||||||
const bool stereoLocks = profile.enhancements.stereo.value() > 0U;
|
|
||||||
const bool locked[EqBandIndex::kBandCount] = {
|
const bool locked[EqBandIndex::kBandCount] = {
|
||||||
true, bassLocks, bassLocks, stereoLocks, stereoLocks, stereoLocks,
|
true, false, false, false, false, false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto& bands = profile.eq.bands();
|
const auto& bands = profile.eq.bands();
|
||||||
@@ -263,9 +264,9 @@ std::expected<AudioProfile, ParseError> parseAudioProfileJson(
|
|||||||
return std::unexpected(ParseError::InvalidJson);
|
return std::unexpected(ParseError::InvalidJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto mixer = parseMixerJson(json);
|
const auto activeSource = parseActiveSourceJson(json);
|
||||||
if (!mixer) {
|
if (!activeSource) {
|
||||||
return std::unexpected(mixer.error());
|
return std::unexpected(activeSource.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto eq = parseEqJson(json);
|
const auto eq = parseEqJson(json);
|
||||||
@@ -290,7 +291,7 @@ std::expected<AudioProfile, ParseError> parseAudioProfileJson(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return AudioProfile{
|
return AudioProfile{
|
||||||
.mixer = *mixer,
|
.activeSource = *activeSource,
|
||||||
.eq = *eq,
|
.eq = *eq,
|
||||||
.masterLeft = *masterLeft,
|
.masterLeft = *masterLeft,
|
||||||
.masterRight = *masterRight,
|
.masterRight = *masterRight,
|
||||||
@@ -337,4 +338,16 @@ std::expected<bool, ParseError> parseBeepEnabledJson(std::string_view json)
|
|||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string serializeAudioLevelsJson(const AudioLevels& levels)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "{\"radio_in_left_db\":" << levels.radioInLeftDb
|
||||||
|
<< ",\"radio_in_right_db\":" << levels.radioInRightDb
|
||||||
|
<< ",\"bluetooth_in_left_db\":" << levels.bluetoothInLeftDb
|
||||||
|
<< ",\"bluetooth_in_right_db\":" << levels.bluetoothInRightDb
|
||||||
|
<< ",\"output_left_db\":" << levels.outputLeftDb
|
||||||
|
<< ",\"output_right_db\":" << levels.outputRightDb << "}";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file EnhancementsDesign.cpp
|
|
||||||
* @brief Enhancement-to-EQ mapping implementation.
|
|
||||||
*
|
|
||||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
|
||||||
*
|
|
||||||
* Copyright 2026 Michele Bigi
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
#include "core/EnhancementsDesign.hpp"
|
|
||||||
|
|
||||||
#include "core/EqBandIndex.hpp"
|
|
||||||
#include "core/FrequencyHz.hpp"
|
|
||||||
#include "core/GainDb.hpp"
|
|
||||||
|
|
||||||
namespace core {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
void setBand(EqProfile& profile, std::uint8_t index, GainDb gain,
|
|
||||||
FrequencyHz center, float q) noexcept
|
|
||||||
{
|
|
||||||
const auto band = EqBandIndex::tryFromIndex(index);
|
|
||||||
if (!band) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
profile.setBand(*band, EqBandSettings{
|
|
||||||
.gain = gain,
|
|
||||||
.center = center,
|
|
||||||
.q = q,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] GainDb gainFromDb(float db) noexcept
|
|
||||||
{
|
|
||||||
return *GainDb::tryFromDb(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
EqProfile applyEnhancementsToEq(const EqProfile& base,
|
|
||||||
const AudioEnhancements& enhancements) noexcept
|
|
||||||
{
|
|
||||||
EqProfile profile = base;
|
|
||||||
|
|
||||||
if (enhancements.bass.value() > 0U) {
|
|
||||||
const float t = enhancements.bass.fraction();
|
|
||||||
setBand(profile, 1U, gainFromDb(9.0F * t),
|
|
||||||
*FrequencyHz::tryFromHz(100U), 0.9F);
|
|
||||||
setBand(profile, 2U, gainFromDb(3.0F * t),
|
|
||||||
*FrequencyHz::tryFromHz(400U), 1.0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enhancements.stereo.value() > 0U) {
|
|
||||||
const float t = enhancements.stereo.fraction();
|
|
||||||
setBand(profile, 3U, gainFromDb(-1.5F * t),
|
|
||||||
*FrequencyHz::tryFromHz(1000U), 1.0F);
|
|
||||||
setBand(profile, 4U, gainFromDb(2.0F * t),
|
|
||||||
*FrequencyHz::tryFromHz(3000U), 1.0F);
|
|
||||||
setBand(profile, 5U, gainFromDb(4.0F * t),
|
|
||||||
*FrequencyHz::tryFromHz(8000U), 1.0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace core
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file MixerState.cpp
|
|
||||||
* @brief MixerState factory defaults.
|
|
||||||
*
|
|
||||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
|
||||||
*
|
|
||||||
* Copyright 2026 Michele Bigi
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "core/MixerState.hpp"
|
|
||||||
|
|
||||||
namespace core {
|
|
||||||
|
|
||||||
MixerState MixerState::factoryDefault() noexcept
|
|
||||||
{
|
|
||||||
const GainDb unity = GainDb::zero();
|
|
||||||
return MixerState{
|
|
||||||
.si4684Left = unity,
|
|
||||||
.si4684Right = unity,
|
|
||||||
.esp32Left = unity,
|
|
||||||
.esp32Right = unity,
|
|
||||||
.mixLeft = unity,
|
|
||||||
.mixRight = unity,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
MixerState MixerState::radioFirst() noexcept
|
|
||||||
{
|
|
||||||
const GainDb unity = GainDb::zero();
|
|
||||||
const GainDb muted = *GainDb::tryFromDb(GainDb::kMinDb);
|
|
||||||
return MixerState{
|
|
||||||
.si4684Left = unity,
|
|
||||||
.si4684Right = unity,
|
|
||||||
.esp32Left = muted,
|
|
||||||
.esp32Right = muted,
|
|
||||||
.mixLeft = unity,
|
|
||||||
.mixRight = muted,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace core
|
|
||||||
@@ -31,11 +31,9 @@ add_library(digiradio_core STATIC
|
|||||||
"${CORE_SRC_DIR}/EqBandIndex.cpp"
|
"${CORE_SRC_DIR}/EqBandIndex.cpp"
|
||||||
"${CORE_SRC_DIR}/BiquadCoefficients.cpp"
|
"${CORE_SRC_DIR}/BiquadCoefficients.cpp"
|
||||||
"${CORE_SRC_DIR}/BiquadDesign.cpp"
|
"${CORE_SRC_DIR}/BiquadDesign.cpp"
|
||||||
"${CORE_SRC_DIR}/MixerState.cpp"
|
|
||||||
"${CORE_SRC_DIR}/EqProfile.cpp"
|
"${CORE_SRC_DIR}/EqProfile.cpp"
|
||||||
"${CORE_SRC_DIR}/EnhanceLevel.cpp"
|
"${CORE_SRC_DIR}/EnhanceLevel.cpp"
|
||||||
"${CORE_SRC_DIR}/AudioEnhancements.cpp"
|
"${CORE_SRC_DIR}/AudioEnhancements.cpp"
|
||||||
"${CORE_SRC_DIR}/EnhancementsDesign.cpp"
|
|
||||||
"${CORE_SRC_DIR}/AudioProfile.cpp"
|
"${CORE_SRC_DIR}/AudioProfile.cpp"
|
||||||
"${CORE_SRC_DIR}/AudioProfileJson.cpp"
|
"${CORE_SRC_DIR}/AudioProfileJson.cpp"
|
||||||
"${CORE_SRC_DIR}/Bt1035At.cpp"
|
"${CORE_SRC_DIR}/Bt1035At.cpp"
|
||||||
@@ -110,10 +108,6 @@ add_executable(audio_profile_json_test audio_profile_json_test.cpp)
|
|||||||
target_link_libraries(audio_profile_json_test PRIVATE digiradio_core)
|
target_link_libraries(audio_profile_json_test PRIVATE digiradio_core)
|
||||||
add_test(NAME audio_profile_json_test COMMAND audio_profile_json_test)
|
add_test(NAME audio_profile_json_test COMMAND audio_profile_json_test)
|
||||||
|
|
||||||
add_executable(enhancements_design_test enhancements_design_test.cpp)
|
|
||||||
target_link_libraries(enhancements_design_test PRIVATE digiradio_core)
|
|
||||||
add_test(NAME enhancements_design_test COMMAND enhancements_design_test)
|
|
||||||
|
|
||||||
add_executable(station_service_test station_service_test.cpp)
|
add_executable(station_service_test station_service_test.cpp)
|
||||||
target_include_directories(station_service_test PRIVATE
|
target_include_directories(station_service_test PRIVATE
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/host_stubs"
|
"${CMAKE_CURRENT_SOURCE_DIR}/host_stubs"
|
||||||
|
|||||||
@@ -69,6 +69,23 @@ namespace {
|
|||||||
std::cerr << "enhancements round-trip mismatch\n";
|
std::cerr << "enhancements round-trip mismatch\n";
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parsed->activeSource != core::ActiveSource::Radio) {
|
||||||
|
std::cerr << "default active_source mismatch\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.activeSource = core::ActiveSource::Bluetooth;
|
||||||
|
const std::string json3 = core::serializeAudioProfileJson(profile);
|
||||||
|
if (json3.find(R"("active_source":"bluetooth")") == std::string::npos) {
|
||||||
|
std::cerr << "active_source serialization mismatch: " << json3 << '\n';
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
const auto parsed3 = core::parseAudioProfileJson(json3);
|
||||||
|
if (!parsed3 || parsed3->activeSource != core::ActiveSource::Bluetooth) {
|
||||||
|
std::cerr << "active_source round-trip mismatch\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file enhancements_design_test.cpp
|
|
||||||
* @brief Host tests for enhancement-to-EQ mapping.
|
|
||||||
*
|
|
||||||
* DigiRadio firmware — https://github.com/manvalan/DigiRadio
|
|
||||||
*
|
|
||||||
* Copyright 2026 Michele Bigi
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "core/AudioEnhancements.hpp"
|
|
||||||
#include "core/EnhanceLevel.hpp"
|
|
||||||
#include "core/EnhancementsDesign.hpp"
|
|
||||||
#include "core/EqBandIndex.hpp"
|
|
||||||
#include "core/EqProfile.hpp"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
[[nodiscard]] bool nearlyEqual(float a, float b) noexcept
|
|
||||||
{
|
|
||||||
return std::fabs(a - b) < 0.01F;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int runBassMappingTest()
|
|
||||||
{
|
|
||||||
const auto level = core::EnhanceLevel::tryFromLevel(100U);
|
|
||||||
if (!level) {
|
|
||||||
std::cerr << "level setup failed\n";
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
core::AudioEnhancements enhancements = core::AudioEnhancements::factoryDefault();
|
|
||||||
enhancements.bass = *level;
|
|
||||||
|
|
||||||
const core::EqProfile base = core::EqProfile::factoryDefault();
|
|
||||||
const core::EqProfile effective =
|
|
||||||
core::applyEnhancementsToEq(base, enhancements);
|
|
||||||
|
|
||||||
const auto band1 = core::EqBandIndex::tryFromIndex(1U);
|
|
||||||
const auto band2 = core::EqBandIndex::tryFromIndex(2U);
|
|
||||||
if (!band1 || !band2) {
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nearlyEqual(effective.band(*band1).gain.value(), 9.0F)
|
|
||||||
|| !nearlyEqual(effective.band(*band2).gain.value(), 3.0F)) {
|
|
||||||
std::cerr << "bass mapping mismatch\n";
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int runStereoMappingTest()
|
|
||||||
{
|
|
||||||
const auto level = core::EnhanceLevel::tryFromLevel(50U);
|
|
||||||
if (!level) {
|
|
||||||
std::cerr << "level setup failed\n";
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
core::AudioEnhancements enhancements = core::AudioEnhancements::factoryDefault();
|
|
||||||
enhancements.stereo = *level;
|
|
||||||
|
|
||||||
const core::EqProfile base = core::EqProfile::factoryDefault();
|
|
||||||
const core::EqProfile effective =
|
|
||||||
core::applyEnhancementsToEq(base, enhancements);
|
|
||||||
|
|
||||||
const auto band3 = core::EqBandIndex::tryFromIndex(3U);
|
|
||||||
const auto band4 = core::EqBandIndex::tryFromIndex(4U);
|
|
||||||
const auto band5 = core::EqBandIndex::tryFromIndex(5U);
|
|
||||||
if (!band3 || !band4 || !band5) {
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nearlyEqual(effective.band(*band3).gain.value(), -0.75F)
|
|
||||||
|| !nearlyEqual(effective.band(*band4).gain.value(), 1.0F)
|
|
||||||
|| !nearlyEqual(effective.band(*band5).gain.value(), 2.0F)) {
|
|
||||||
std::cerr << "stereo mapping mismatch\n";
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int runOffLeavesBaseTest()
|
|
||||||
{
|
|
||||||
const core::EqProfile base = core::EqProfile::factoryDefault();
|
|
||||||
const core::AudioEnhancements off = core::AudioEnhancements::factoryDefault();
|
|
||||||
const core::EqProfile effective = core::applyEnhancementsToEq(base, off);
|
|
||||||
|
|
||||||
const auto band1 = core::EqBandIndex::tryFromIndex(1U);
|
|
||||||
if (!band1) {
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nearlyEqual(effective.band(*band1).gain.value(),
|
|
||||||
base.band(*band1).gain.value())) {
|
|
||||||
std::cerr << "off should leave base unchanged\n";
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
if (runBassMappingTest() != EXIT_SUCCESS) {
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (runStereoMappingTest() != EXIT_SUCCESS) {
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (runOffLeavesBaseTest() != EXIT_SUCCESS) {
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
@@ -40,8 +40,8 @@ public:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> applyMixer(
|
[[nodiscard]] std::expected<void, core::DspError> selectSource(
|
||||||
const core::MixerState&) override
|
core::ActiveSource) override
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -52,12 +52,6 @@ public:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> setInputVolume(
|
|
||||||
core::MixSource, core::GainDb, core::GainDb) override
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> setMasterVolume(
|
[[nodiscard]] std::expected<void, core::DspError> setMasterVolume(
|
||||||
core::GainDb, core::GainDb) override
|
core::GainDb, core::GainDb) override
|
||||||
{
|
{
|
||||||
@@ -76,6 +70,24 @@ public:
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, core::DspError> setBassBoostLevel(
|
||||||
|
core::EnhanceLevel) override
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, core::DspError> setStereoSpreadLevel(
|
||||||
|
core::EnhanceLevel) override
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<core::AudioLevels, core::DspError>
|
||||||
|
readLevels() override
|
||||||
|
{
|
||||||
|
return core::AudioLevels{};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
||||||
unsigned, float) override
|
unsigned, float) override
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,14 +14,15 @@
|
|||||||
|
|
||||||
#include "adau1701/Adau1701Error.hpp"
|
#include "adau1701/Adau1701Error.hpp"
|
||||||
|
|
||||||
|
#include "core/AudioLevels.hpp"
|
||||||
#include "core/AudioProfile.hpp"
|
#include "core/AudioProfile.hpp"
|
||||||
|
#include "core/EnhanceLevel.hpp"
|
||||||
#include "core/EqBandIndex.hpp"
|
#include "core/EqBandIndex.hpp"
|
||||||
#include "core/EqProfile.hpp"
|
#include "core/EqProfile.hpp"
|
||||||
#include "core/FrequencyHz.hpp"
|
#include "core/FrequencyHz.hpp"
|
||||||
#include "core/GainDb.hpp"
|
#include "core/GainDb.hpp"
|
||||||
#include "core/IDspProgramSource.hpp"
|
#include "core/IDspProgramSource.hpp"
|
||||||
#include "core/MixSource.hpp"
|
#include "core/ActiveSource.hpp"
|
||||||
#include "core/MixerState.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
@@ -144,18 +145,18 @@ public:
|
|||||||
const core::AudioProfile& profile);
|
const core::AudioProfile& profile);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief applyMixer — safeload input and stereo-mixer gains.
|
* @brief selectSource — safeload DC1 to pick MX1's active input pair.
|
||||||
*
|
*
|
||||||
* @dname applyMixer
|
* @dname selectSource
|
||||||
* @param mixer Per-source and St Mixer1 levels.
|
* @param source Which stereo pair MX1 passes through.
|
||||||
* @return Ok on success, or Adau1701Error.
|
* @return Ok on success, or Adau1701Error.
|
||||||
* @pubstate writes parameter RAM via safeload.
|
* @pubstate writes parameter RAM via safeload.
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
* @date 2026-07-06
|
* @date 2026-08-25
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::expected<void, Adau1701Error> applyMixer(
|
[[nodiscard]] std::expected<void, Adau1701Error> selectSource(
|
||||||
const core::MixerState& mixer);
|
core::ActiveSource source);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief applyEq — safeload all six PEQ bands.
|
* @brief applyEq — safeload all six PEQ bands.
|
||||||
@@ -171,22 +172,6 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, Adau1701Error> applyEq(
|
[[nodiscard]] std::expected<void, Adau1701Error> applyEq(
|
||||||
const core::EqProfile& eq);
|
const core::EqProfile& eq);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief setInputVolume — safeload one input path gain.
|
|
||||||
*
|
|
||||||
* @dname setInputVolume
|
|
||||||
* @param source Si4684 or ESP32 I2S path.
|
|
||||||
* @param left Left channel gain.
|
|
||||||
* @param right Right channel gain.
|
|
||||||
* @return Ok on success, or Adau1701Error.
|
|
||||||
* @pubstate writes parameter RAM via safeload.
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
[[nodiscard]] std::expected<void, Adau1701Error> setInputVolume(
|
|
||||||
core::MixSource source, core::GainDb left, core::GainDb right);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief setMasterVolume — safeload Multiple 1 master output gain.
|
* @brief setMasterVolume — safeload Multiple 1 master output gain.
|
||||||
*
|
*
|
||||||
@@ -234,6 +219,35 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, Adau1701Error> setBeepEnabled(
|
[[nodiscard]] std::expected<void, Adau1701Error> setBeepEnabled(
|
||||||
bool enabled);
|
bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief setBassBoostLevel — scale Bass Boost1 toward its compiled
|
||||||
|
* curve.
|
||||||
|
*
|
||||||
|
* @dname setBassBoostLevel
|
||||||
|
* @param level 0 = flat bypass, 100 = full compiled response.
|
||||||
|
* @return Ok on success, or Adau1701Error.
|
||||||
|
* @pubstate writes parameter RAM via safeload.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::expected<void, Adau1701Error> setBassBoostLevel(
|
||||||
|
core::EnhanceLevel level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief setStereoSpreadLevel — scale SPhat1's stereo spread amount.
|
||||||
|
*
|
||||||
|
* @dname setStereoSpreadLevel
|
||||||
|
* @param level 0 = no spread, 100 = full compiled spread.
|
||||||
|
* @return Ok on success, or Adau1701Error.
|
||||||
|
* @pubstate writes parameter RAM via safeload.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::expected<void, Adau1701Error> setStereoSpreadLevel(
|
||||||
|
core::EnhanceLevel level);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief writeRawParam — safeload any named Parameter RAM cell.
|
* @brief writeRawParam — safeload any named Parameter RAM cell.
|
||||||
*
|
*
|
||||||
@@ -256,6 +270,21 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, Adau1701Error> writeRawParam(
|
[[nodiscard]] std::expected<void, Adau1701Error> writeRawParam(
|
||||||
unsigned address, float value);
|
unsigned address, float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief readLevels — on-demand read of all six 1×RTA level meters.
|
||||||
|
*
|
||||||
|
* @dname readLevels
|
||||||
|
* @return AudioLevels snapshot, or Adau1701Error.
|
||||||
|
* @pubstate Reads the ADAU1701 Data Capture Register (address 2074) six
|
||||||
|
* times in sequence, reconfiguring it for each meter's
|
||||||
|
* program-step/register-select pair before each read; no
|
||||||
|
* background polling, only runs when called.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::expected<core::AudioLevels, Adau1701Error> readLevels();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] std::expected<void, Adau1701Error> ensureBooted() const;
|
[[nodiscard]] std::expected<void, Adau1701Error> ensureBooted() const;
|
||||||
[[nodiscard]] std::expected<void, Adau1701Error> safeloadGain(
|
[[nodiscard]] std::expected<void, Adau1701Error> safeloadGain(
|
||||||
@@ -264,6 +293,8 @@ private:
|
|||||||
unsigned paramAddr, std::int32_t fixpoint);
|
unsigned paramAddr, std::int32_t fixpoint);
|
||||||
[[nodiscard]] std::expected<void, Adau1701Error> replayProgram(
|
[[nodiscard]] std::expected<void, Adau1701Error> replayProgram(
|
||||||
const core::DspProgram& program);
|
const core::DspProgram& program);
|
||||||
|
[[nodiscard]] std::expected<float, Adau1701Error> readCaptureDb(
|
||||||
|
unsigned progCount, unsigned regSel);
|
||||||
|
|
||||||
Adau1701Pins pins_;
|
Adau1701Pins pins_;
|
||||||
core::IDspProgramSource& programSource_;
|
core::IDspProgramSource& programSource_;
|
||||||
|
|||||||
@@ -45,15 +45,12 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, core::DspError> applyProfile(
|
[[nodiscard]] std::expected<void, core::DspError> applyProfile(
|
||||||
const core::AudioProfile& profile) override;
|
const core::AudioProfile& profile) override;
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> applyMixer(
|
[[nodiscard]] std::expected<void, core::DspError> selectSource(
|
||||||
const core::MixerState& mixer) override;
|
core::ActiveSource source) override;
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> applyEq(
|
[[nodiscard]] std::expected<void, core::DspError> applyEq(
|
||||||
const core::EqProfile& eq) override;
|
const core::EqProfile& eq) override;
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> setInputVolume(
|
|
||||||
core::MixSource source, core::GainDb left, core::GainDb right) override;
|
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> setMasterVolume(
|
[[nodiscard]] std::expected<void, core::DspError> setMasterVolume(
|
||||||
core::GainDb left, core::GainDb right) override;
|
core::GainDb left, core::GainDb right) override;
|
||||||
|
|
||||||
@@ -64,9 +61,18 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, core::DspError> setBeepEnabled(
|
[[nodiscard]] std::expected<void, core::DspError> setBeepEnabled(
|
||||||
bool enabled) override;
|
bool enabled) override;
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, core::DspError> setBassBoostLevel(
|
||||||
|
core::EnhanceLevel level) override;
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<void, core::DspError> setStereoSpreadLevel(
|
||||||
|
core::EnhanceLevel level) override;
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
||||||
unsigned address, float value) override;
|
unsigned address, float value) override;
|
||||||
|
|
||||||
|
[[nodiscard]] std::expected<core::AudioLevels, core::DspError>
|
||||||
|
readLevels() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static core::DspError mapError(Adau1701Error error) noexcept;
|
[[nodiscard]] static core::DspError mapError(Adau1701Error error) noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
#include "DigiRadio_IC_1_PARAM.h"
|
#include "DigiRadio_IC_1_PARAM.h"
|
||||||
|
|
||||||
|
#include "core/ActiveSource.hpp"
|
||||||
#include "core/EqBandIndex.hpp"
|
#include "core/EqBandIndex.hpp"
|
||||||
#include "core/MixSource.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@@ -39,39 +39,30 @@ namespace adau1701 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief paramAddrInputLeft — left volume address for an input source.
|
* @brief paramSourceIndex — MX1 pair index written to DC1 for a source.
|
||||||
*
|
*
|
||||||
* @dname paramAddrInputLeft
|
* @dname paramSourceIndex
|
||||||
* @param source Si4684 or ESP32 path.
|
* @param source Which stereo pair MX1 should pass through.
|
||||||
* @return Parameter RAM address for the left channel gain cell.
|
* @return 0 (Radio), 1 (Bluetooth), or 2 (Beep) -- confirmed live
|
||||||
|
* 2026-08-25 against real hardware (see
|
||||||
|
* docs/adau1701-sigmastudio-analysis.md).
|
||||||
* @pubstate none
|
* @pubstate none
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
* @date 2026-07-06
|
* @date 2026-08-25
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] inline unsigned paramAddrInputLeft(core::MixSource source) noexcept
|
[[nodiscard]] inline unsigned paramSourceIndex(
|
||||||
|
core::ActiveSource source) noexcept
|
||||||
{
|
{
|
||||||
return source == core::MixSource::Si4684
|
switch (source) {
|
||||||
? static_cast<unsigned>(ADDR_SI4674)
|
case core::ActiveSource::Radio:
|
||||||
: static_cast<unsigned>(ADDR_ESP32);
|
return 0U;
|
||||||
}
|
case core::ActiveSource::Bluetooth:
|
||||||
|
return 1U;
|
||||||
/**
|
case core::ActiveSource::Beep:
|
||||||
* @brief paramAddrInputRight — right volume address for an input source.
|
return 2U;
|
||||||
*
|
}
|
||||||
* @dname paramAddrInputRight
|
return 0U;
|
||||||
* @param source Si4684 or ESP32 path.
|
|
||||||
* @return Parameter RAM address for the right channel gain cell.
|
|
||||||
* @pubstate none
|
|
||||||
*
|
|
||||||
* @author Michele Bigi
|
|
||||||
* @date 2026-07-06
|
|
||||||
*/
|
|
||||||
[[nodiscard]] inline unsigned paramAddrInputRight(core::MixSource source) noexcept
|
|
||||||
{
|
|
||||||
return source == core::MixSource::Si4684
|
|
||||||
? static_cast<unsigned>(ADDR_SI4674_1)
|
|
||||||
: static_cast<unsigned>(ADDR_ESP32_1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace adau1701
|
} // namespace adau1701
|
||||||
|
|||||||
@@ -8,13 +8,14 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*
|
*
|
||||||
* Generated from Firmware/ADAU1701-Firmware/DigiRadio_IC_1_PARAM.h (the
|
* Generated from Firmware/ADAU1701-Firmware/DigiRadio_IC_1_PARAM.h (the
|
||||||
* SigmaStudio export). Regenerate by re-running the extraction over that
|
* SigmaStudio export, "DigiRadioFinale" revision, captured 2026-08-25).
|
||||||
* file if the SigmaStudio project's cell list changes; entries here must
|
* Regenerate by re-running the extraction over that file if the SigmaStudio
|
||||||
* stay in sync with the ADDR_* constants used by
|
* project's cell list changes; entries here must stay in sync with the
|
||||||
|
* ADDR_* constants used by
|
||||||
* components/drivers/adau1701/src/Adau1701Driver.cpp's replayProgram().
|
* components/drivers/adau1701/src/Adau1701Driver.cpp's replayProgram().
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
* @date 2026-08-18
|
* @date 2026-08-25
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -39,83 +40,239 @@ struct Adau1701ParamEntry {
|
|||||||
* @pubstate Read-only; see file header for regeneration instructions.
|
* @pubstate Read-only; see file header for regeneration instructions.
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
* @date 2026-08-18
|
* @date 2026-08-25
|
||||||
*/
|
*/
|
||||||
inline constexpr std::array<Adau1701ParamEntry, 74> kAdau1701ParamTable = {{
|
inline constexpr std::array<Adau1701ParamEntry, 230> kAdau1701ParamTable = {{
|
||||||
{"BEEP1_ENABLE", 0},
|
{"BEEP1_ENABLE", 0},
|
||||||
{"BEEP1_KICK", 1},
|
{"BEEP1_KICK", 1},
|
||||||
{"BEEP1_BEEP_FREQ", 2},
|
{"BEEP1_BEEP_FREQ", 2},
|
||||||
{"SI4674", 3},
|
{"DC1", 3},
|
||||||
{"SI4674_1", 4},
|
{"DCB1_POLE", 4},
|
||||||
{"ESP32", 5},
|
{"DCB2_POLE", 5},
|
||||||
{"ESP32_1", 6},
|
{"DCB4_POLE", 6},
|
||||||
{"SINGLE1", 7},
|
{"DCB3_POLE", 7},
|
||||||
{"SSPLITTER1", 8},
|
{"SSPLITTER1", 8},
|
||||||
{"STMIXER1_ST0_VOLUME", 9},
|
{"COMPRESSOR1_0", 9},
|
||||||
{"STMIXER1_ST1_VOLUME", 10},
|
{"COMPRESSOR1_1", 10},
|
||||||
{"STMIXER1_ST2_VOLUME", 11},
|
{"COMPRESSOR1_2", 11},
|
||||||
{"PARAMEQ1_ST0_B0", 12},
|
{"COMPRESSOR1_3", 12},
|
||||||
{"PARAMEQ1_ST0_B1", 13},
|
{"COMPRESSOR1_4", 13},
|
||||||
{"PARAMEQ1_ST0_B2", 14},
|
{"COMPRESSOR1_5", 14},
|
||||||
{"PARAMEQ1_ST0_A0", 15},
|
{"COMPRESSOR1_6", 15},
|
||||||
{"PARAMEQ1_ST0_A1", 16},
|
{"COMPRESSOR1_7", 16},
|
||||||
{"PARAMEQ1_ST1_B0", 17},
|
{"COMPRESSOR1_8", 17},
|
||||||
{"PARAMEQ1_ST1_B1", 18},
|
{"COMPRESSOR1_9", 18},
|
||||||
{"PARAMEQ1_ST1_B2", 19},
|
{"COMPRESSOR1_10", 19},
|
||||||
{"PARAMEQ1_ST1_A0", 20},
|
{"COMPRESSOR1_11", 20},
|
||||||
{"PARAMEQ1_ST1_A1", 21},
|
{"COMPRESSOR1_12", 21},
|
||||||
{"PARAMEQ1_ST2_B0", 22},
|
{"COMPRESSOR1_13", 22},
|
||||||
{"PARAMEQ1_ST2_B1", 23},
|
{"COMPRESSOR1_14", 23},
|
||||||
{"PARAMEQ1_ST2_B2", 24},
|
{"COMPRESSOR1_15", 24},
|
||||||
{"PARAMEQ1_ST2_A0", 25},
|
{"COMPRESSOR1_16", 25},
|
||||||
{"PARAMEQ1_ST2_A1", 26},
|
{"COMPRESSOR1_17", 26},
|
||||||
{"PARAMEQ1_ST3_B0", 27},
|
{"COMPRESSOR1_18", 27},
|
||||||
{"PARAMEQ1_ST3_B1", 28},
|
{"COMPRESSOR1_19", 28},
|
||||||
{"PARAMEQ1_ST3_B2", 29},
|
{"COMPRESSOR1_20", 29},
|
||||||
{"PARAMEQ1_ST3_A0", 30},
|
{"COMPRESSOR1_21", 30},
|
||||||
{"PARAMEQ1_ST3_A1", 31},
|
{"COMPRESSOR1_22", 31},
|
||||||
{"PARAMEQ1_ST4_B0", 32},
|
{"COMPRESSOR1_23", 32},
|
||||||
{"PARAMEQ1_ST4_B1", 33},
|
{"COMPRESSOR1_24", 33},
|
||||||
{"PARAMEQ1_ST4_B2", 34},
|
{"COMPRESSOR1_25", 34},
|
||||||
{"PARAMEQ1_ST4_A0", 35},
|
{"COMPRESSOR1_26", 35},
|
||||||
{"PARAMEQ1_ST4_A1", 36},
|
{"COMPRESSOR1_27", 36},
|
||||||
{"PARAMEQ1_ST5_B0", 37},
|
{"COMPRESSOR1_28", 37},
|
||||||
{"PARAMEQ1_ST5_B1", 38},
|
{"COMPRESSOR1_29", 38},
|
||||||
{"PARAMEQ1_ST5_B2", 39},
|
{"COMPRESSOR1_30", 39},
|
||||||
{"PARAMEQ1_ST5_A0", 40},
|
{"COMPRESSOR1_31", 40},
|
||||||
{"PARAMEQ1_ST5_A1", 41},
|
{"COMPRESSOR1_32", 41},
|
||||||
{"MULTIPLE1", 42},
|
{"COMPRESSOR1_33", 42},
|
||||||
{"MULTIPLE1_1", 43},
|
{"COMPRESSOR1_RMS", 43},
|
||||||
{"LIMITER1_S1", 44},
|
{"COMPRESSOR1_POST_GAIN", 44},
|
||||||
{"LIMITER1_INT1", 45},
|
{"COMPRESSOR1_HOLD", 45},
|
||||||
{"LIMITER1_S2", 46},
|
{"COMPRESSOR1_DECAY", 46},
|
||||||
{"LIMITER1_INT2", 47},
|
{"1XRTA3_TCONST", 47},
|
||||||
{"LIMITER1_S3", 48},
|
{"1XRTA3_HOLD", 48},
|
||||||
{"LIMITER1_INT3", 49},
|
{"1XRTA3_DECAY", 49},
|
||||||
{"LIMITER1_S4", 50},
|
{"1XRTA3", 2074},
|
||||||
{"LIMITER1_INT4", 51},
|
{"1XRTA4_TCONST", 50},
|
||||||
{"LIMITER1_C1", 52},
|
{"1XRTA4_HOLD", 51},
|
||||||
{"LIMITER1_C2", 53},
|
{"1XRTA4_DECAY", 52},
|
||||||
{"LIMITER1_C3", 54},
|
{"1XRTA4", 2074},
|
||||||
{"LIMITER1_THRESHOLD", 55},
|
{"1XRTA1_TCONST", 53},
|
||||||
{"LIMITER1_RMS", 56},
|
{"1XRTA1_HOLD", 54},
|
||||||
{"LIMITER1_DECAY", 57},
|
{"1XRTA1_DECAY", 55},
|
||||||
{"LIMITER1_DECAYCOMPLEMENT", 58},
|
{"1XRTA1", 2074},
|
||||||
{"LIMITER2_S1", 59},
|
{"1XRTA2_TCONST", 56},
|
||||||
{"LIMITER2_INT1", 60},
|
{"1XRTA2_HOLD", 57},
|
||||||
{"LIMITER2_S2", 61},
|
{"1XRTA2_DECAY", 58},
|
||||||
{"LIMITER2_INT2", 62},
|
{"1XRTA2", 2074},
|
||||||
{"LIMITER2_S3", 63},
|
{"PARAMEQ1_ST0_B0", 59},
|
||||||
{"LIMITER2_INT3", 64},
|
{"PARAMEQ1_ST0_B1", 60},
|
||||||
{"LIMITER2_S4", 65},
|
{"PARAMEQ1_ST0_B2", 61},
|
||||||
{"LIMITER2_INT4", 66},
|
{"PARAMEQ1_ST0_A0", 62},
|
||||||
{"LIMITER2_C1", 67},
|
{"PARAMEQ1_ST0_A1", 63},
|
||||||
{"LIMITER2_C2", 68},
|
{"PARAMEQ1_ST1_B0", 64},
|
||||||
{"LIMITER2_C3", 69},
|
{"PARAMEQ1_ST1_B1", 65},
|
||||||
{"LIMITER2_THRESHOLD", 70},
|
{"PARAMEQ1_ST1_B2", 66},
|
||||||
{"LIMITER2_RMS", 71},
|
{"PARAMEQ1_ST1_A0", 67},
|
||||||
{"LIMITER2_DECAY", 72},
|
{"PARAMEQ1_ST1_A1", 68},
|
||||||
{"LIMITER2_DECAYCOMPLEMENT", 73},
|
{"PARAMEQ1_ST2_B0", 69},
|
||||||
|
{"PARAMEQ1_ST2_B1", 70},
|
||||||
|
{"PARAMEQ1_ST2_B2", 71},
|
||||||
|
{"PARAMEQ1_ST2_A0", 72},
|
||||||
|
{"PARAMEQ1_ST2_A1", 73},
|
||||||
|
{"PARAMEQ1_ST3_B0", 74},
|
||||||
|
{"PARAMEQ1_ST3_B1", 75},
|
||||||
|
{"PARAMEQ1_ST3_B2", 76},
|
||||||
|
{"PARAMEQ1_ST3_A0", 77},
|
||||||
|
{"PARAMEQ1_ST3_A1", 78},
|
||||||
|
{"PARAMEQ1_ST4_B0", 79},
|
||||||
|
{"PARAMEQ1_ST4_B1", 80},
|
||||||
|
{"PARAMEQ1_ST4_B2", 81},
|
||||||
|
{"PARAMEQ1_ST4_A0", 82},
|
||||||
|
{"PARAMEQ1_ST4_A1", 83},
|
||||||
|
{"PARAMEQ1_ST5_B0", 84},
|
||||||
|
{"PARAMEQ1_ST5_B1", 85},
|
||||||
|
{"PARAMEQ1_ST5_B2", 86},
|
||||||
|
{"PARAMEQ1_ST5_A0", 87},
|
||||||
|
{"PARAMEQ1_ST5_A1", 88},
|
||||||
|
{"SPHAT1_B0LO0", 89},
|
||||||
|
{"SPHAT1_B0LO1", 90},
|
||||||
|
{"SPHAT1_B0LO2", 91},
|
||||||
|
{"SPHAT1_B0LO3", 92},
|
||||||
|
{"SPHAT1_B0LO4", 93},
|
||||||
|
{"SPHAT1_B0LO5", 94},
|
||||||
|
{"SPHAT1_B0LO6", 95},
|
||||||
|
{"SPHAT1_B0LO7", 96},
|
||||||
|
{"SPHAT1_B0LO8", 97},
|
||||||
|
{"SPHAT1_B0LO9", 98},
|
||||||
|
{"SPHAT1_GAINHI", 99},
|
||||||
|
{"SPHAT1_GAINLO", 100},
|
||||||
|
{"SPHAT1_GAININV", 101},
|
||||||
|
{"SPHAT1_COMP_BASE0", 102},
|
||||||
|
{"SPHAT1_COMP_BASE1", 103},
|
||||||
|
{"SPHAT1_COMP_BASE2", 104},
|
||||||
|
{"SPHAT1_COMP_BASE3", 105},
|
||||||
|
{"SPHAT1_COMP_BASE4", 106},
|
||||||
|
{"SPHAT1_COMP_BASE5", 107},
|
||||||
|
{"SPHAT1_COMP_BASE6", 108},
|
||||||
|
{"SPHAT1_COMP_BASE7", 109},
|
||||||
|
{"SPHAT1_COMP_BASE8", 110},
|
||||||
|
{"SPHAT1_COMP_BASE9", 111},
|
||||||
|
{"SPHAT1_COMP_BASE10", 112},
|
||||||
|
{"SPHAT1_COMP_BASE11", 113},
|
||||||
|
{"SPHAT1_COMP_BASE12", 114},
|
||||||
|
{"SPHAT1_COMP_BASE13", 115},
|
||||||
|
{"SPHAT1_COMP_BASE14", 116},
|
||||||
|
{"SPHAT1_COMP_BASE15", 117},
|
||||||
|
{"SPHAT1_COMP_BASE16", 118},
|
||||||
|
{"SPHAT1_COMP_BASE17", 119},
|
||||||
|
{"SPHAT1_COMP_BASE18", 120},
|
||||||
|
{"SPHAT1_COMP_BASE19", 121},
|
||||||
|
{"SPHAT1_COMP_BASE20", 122},
|
||||||
|
{"SPHAT1_COMP_BASE21", 123},
|
||||||
|
{"SPHAT1_COMP_BASE22", 124},
|
||||||
|
{"SPHAT1_COMP_BASE23", 125},
|
||||||
|
{"SPHAT1_COMP_BASE24", 126},
|
||||||
|
{"SPHAT1_COMP_BASE25", 127},
|
||||||
|
{"SPHAT1_COMP_BASE26", 128},
|
||||||
|
{"SPHAT1_COMP_BASE27", 129},
|
||||||
|
{"SPHAT1_COMP_BASE28", 130},
|
||||||
|
{"SPHAT1_COMP_BASE29", 131},
|
||||||
|
{"SPHAT1_COMP_BASE30", 132},
|
||||||
|
{"SPHAT1_COMP_BASE31", 133},
|
||||||
|
{"SPHAT1_COMP_BASE32", 134},
|
||||||
|
{"SPHAT1_COMP_BASE33", 135},
|
||||||
|
{"SPHAT1_RMS", 136},
|
||||||
|
{"SPHAT1_DECAY", 137},
|
||||||
|
{"SPHAT1_HOLD", 138},
|
||||||
|
{"SPHAT1_SPREAD1", 139},
|
||||||
|
{"SPHAT1_SPREAD2", 140},
|
||||||
|
{"GENFILTER1_ST0_B0", 141},
|
||||||
|
{"GENFILTER1_ST0_B1", 142},
|
||||||
|
{"GENFILTER1_ST0_B2", 143},
|
||||||
|
{"GENFILTER1_ST0_A1", 144},
|
||||||
|
{"GENFILTER1_ST0_A2", 145},
|
||||||
|
{"MULTIPLE1", 146},
|
||||||
|
{"MULTIPLE1_1", 147},
|
||||||
|
{"BASSBOOST1_BASSFREQUENCY", 148},
|
||||||
|
{"BASSBOOST1_B0", 149},
|
||||||
|
{"BASSBOOST1_B1", 150},
|
||||||
|
{"BASSBOOST1_B2", 151},
|
||||||
|
{"BASSBOOST1_A1", 152},
|
||||||
|
{"BASSBOOST1_A2", 153},
|
||||||
|
{"BASSBOOST1_TABLE0", 154},
|
||||||
|
{"BASSBOOST1_TABLE1", 155},
|
||||||
|
{"BASSBOOST1_TABLE2", 156},
|
||||||
|
{"BASSBOOST1_TABLE3", 157},
|
||||||
|
{"BASSBOOST1_TABLE4", 158},
|
||||||
|
{"BASSBOOST1_TABLE5", 159},
|
||||||
|
{"BASSBOOST1_TABLE6", 160},
|
||||||
|
{"BASSBOOST1_TABLE7", 161},
|
||||||
|
{"BASSBOOST1_TABLE8", 162},
|
||||||
|
{"BASSBOOST1_TABLE9", 163},
|
||||||
|
{"BASSBOOST1_TABLE10", 164},
|
||||||
|
{"BASSBOOST1_TABLE11", 165},
|
||||||
|
{"BASSBOOST1_TABLE12", 166},
|
||||||
|
{"BASSBOOST1_TABLE13", 167},
|
||||||
|
{"BASSBOOST1_TABLE14", 168},
|
||||||
|
{"BASSBOOST1_TABLE15", 169},
|
||||||
|
{"BASSBOOST1_TABLE16", 170},
|
||||||
|
{"BASSBOOST1_TABLE17", 171},
|
||||||
|
{"BASSBOOST1_TABLE18", 172},
|
||||||
|
{"BASSBOOST1_TABLE19", 173},
|
||||||
|
{"BASSBOOST1_TABLE20", 174},
|
||||||
|
{"BASSBOOST1_TABLE21", 175},
|
||||||
|
{"BASSBOOST1_TABLE22", 176},
|
||||||
|
{"BASSBOOST1_TABLE23", 177},
|
||||||
|
{"BASSBOOST1_TABLE24", 178},
|
||||||
|
{"BASSBOOST1_TABLE25", 179},
|
||||||
|
{"BASSBOOST1_TABLE26", 180},
|
||||||
|
{"BASSBOOST1_TABLE27", 181},
|
||||||
|
{"BASSBOOST1_TABLE28", 182},
|
||||||
|
{"BASSBOOST1_TABLE29", 183},
|
||||||
|
{"BASSBOOST1_TABLE30", 184},
|
||||||
|
{"BASSBOOST1_TABLE31", 185},
|
||||||
|
{"BASSBOOST1_TABLE32", 186},
|
||||||
|
{"BASSBOOST1_TIMECONSTANT", 187},
|
||||||
|
{"1XRTA2_2_TCONST", 188},
|
||||||
|
{"1XRTA2_2_HOLD", 189},
|
||||||
|
{"1XRTA2_2_DECAY", 190},
|
||||||
|
{"1XRTA2_2", 2074},
|
||||||
|
{"1XRTA1_2_TCONST", 191},
|
||||||
|
{"1XRTA1_2_HOLD", 192},
|
||||||
|
{"1XRTA1_2_DECAY", 193},
|
||||||
|
{"1XRTA1_2", 2074},
|
||||||
|
{"LIMITER2_S1", 194},
|
||||||
|
{"LIMITER2_INT1", 195},
|
||||||
|
{"LIMITER2_S2", 196},
|
||||||
|
{"LIMITER2_INT2", 197},
|
||||||
|
{"LIMITER2_S3", 198},
|
||||||
|
{"LIMITER2_INT3", 199},
|
||||||
|
{"LIMITER2_S4", 200},
|
||||||
|
{"LIMITER2_INT4", 201},
|
||||||
|
{"LIMITER2_C1", 202},
|
||||||
|
{"LIMITER2_C2", 203},
|
||||||
|
{"LIMITER2_C3", 204},
|
||||||
|
{"LIMITER2_THRESHOLD", 205},
|
||||||
|
{"LIMITER2_RMS", 206},
|
||||||
|
{"LIMITER2_DECAY", 207},
|
||||||
|
{"LIMITER2_DECAYCOMPLEMENT", 208},
|
||||||
|
{"LIMITER1_S1", 209},
|
||||||
|
{"LIMITER1_INT1", 210},
|
||||||
|
{"LIMITER1_S2", 211},
|
||||||
|
{"LIMITER1_INT2", 212},
|
||||||
|
{"LIMITER1_S3", 213},
|
||||||
|
{"LIMITER1_INT3", 214},
|
||||||
|
{"LIMITER1_S4", 215},
|
||||||
|
{"LIMITER1_INT4", 216},
|
||||||
|
{"LIMITER1_C1", 217},
|
||||||
|
{"LIMITER1_C2", 218},
|
||||||
|
{"LIMITER1_C3", 219},
|
||||||
|
{"LIMITER1_THRESHOLD", 220},
|
||||||
|
{"LIMITER1_RMS", 221},
|
||||||
|
{"LIMITER1_DECAY", 222},
|
||||||
|
{"LIMITER1_DECAYCOMPLEMENT", 223},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,7 +284,7 @@ inline constexpr std::array<Adau1701ParamEntry, 74> kAdau1701ParamTable = {{
|
|||||||
* @pubstate none
|
* @pubstate none
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
* @date 2026-08-18
|
* @date 2026-08-25
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] inline std::optional<unsigned> findAdau1701ParamAddress(
|
[[nodiscard]] inline std::optional<unsigned> findAdau1701ParamAddress(
|
||||||
std::string_view name) noexcept
|
std::string_view name) noexcept
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
#include "core/BiquadDesign.hpp"
|
#include "core/BiquadDesign.hpp"
|
||||||
#include "core/DspProgram.hpp"
|
#include "core/DspProgram.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "DigiRadio_IC_1_PARAM.h"
|
#include "DigiRadio_IC_1_PARAM.h"
|
||||||
#include "SigmaStudioFW.h"
|
#include "SigmaStudioFW.h"
|
||||||
|
|
||||||
@@ -358,18 +360,22 @@ namespace adau1701
|
|||||||
return safeloadFixpoint(paramAddr, core::gainDbToLinearFixpoint(gain));
|
return safeloadFixpoint(paramAddr, core::gainDbToLinearFixpoint(gain));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, Adau1701Error> Adau1701Driver::setInputVolume(
|
std::expected<void, Adau1701Error> Adau1701Driver::selectSource(
|
||||||
core::MixSource source, core::GainDb left, core::GainDb right)
|
core::ActiveSource source)
|
||||||
{
|
{
|
||||||
if (auto ready = ensureBooted(); !ready)
|
if (auto ready = ensureBooted(); !ready)
|
||||||
{
|
{
|
||||||
return ready;
|
return ready;
|
||||||
}
|
}
|
||||||
if (auto result = safeloadGain(paramAddrInputLeft(source), left); !result)
|
// MX1 (DCinputmux_stereo) reads DC1 as a raw 32-bit integer pair
|
||||||
{
|
// index (0/1/2), NOT the 5.23 fixpoint conversion its compiled
|
||||||
return result;
|
// TYPE_DC1/VALUE_DC1 macros would suggest -- confirmed live
|
||||||
}
|
// 2026-08-25 (a 5.23-encoded write left the source unchanged; the
|
||||||
return safeloadGain(paramAddrInputRight(source), right);
|
// raw integer switched Radio/Bluetooth/Beep correctly). 0=Radio,
|
||||||
|
// 1=Bluetooth (ESP32), 2=Beep, per paramSourceIndex().
|
||||||
|
const auto index =
|
||||||
|
static_cast<std::int32_t>(paramSourceIndex(source));
|
||||||
|
return safeloadFixpoint(static_cast<unsigned>(ADDR_DC1), index);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, Adau1701Error> Adau1701Driver::setMasterVolume(
|
std::expected<void, Adau1701Error> Adau1701Driver::setMasterVolume(
|
||||||
@@ -387,45 +393,6 @@ namespace adau1701
|
|||||||
return safeloadGain(static_cast<unsigned>(ADDR_MULTIPLE1_1), right);
|
return safeloadGain(static_cast<unsigned>(ADDR_MULTIPLE1_1), right);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, Adau1701Error> Adau1701Driver::applyMixer(
|
|
||||||
const core::MixerState &mixer)
|
|
||||||
{
|
|
||||||
if (auto ready = ensureBooted(); !ready)
|
|
||||||
{
|
|
||||||
return ready;
|
|
||||||
}
|
|
||||||
if (auto result = setInputVolume(core::MixSource::Si4684, mixer.si4684Left,
|
|
||||||
mixer.si4684Right);
|
|
||||||
!result)
|
|
||||||
{
|
|
||||||
ESP_LOGW(kTag, "applyMixer: si4684 input safeload failed");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (auto result = setInputVolume(core::MixSource::Esp32, mixer.esp32Left,
|
|
||||||
mixer.esp32Right);
|
|
||||||
!result)
|
|
||||||
{
|
|
||||||
ESP_LOGW(kTag, "applyMixer: esp32 input safeload failed");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (auto result =
|
|
||||||
safeloadGain(static_cast<unsigned>(ADDR_STMIXER1_ST0_VOLUME),
|
|
||||||
mixer.mixLeft);
|
|
||||||
!result)
|
|
||||||
{
|
|
||||||
ESP_LOGW(kTag, "applyMixer: st0 safeload failed");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (auto result = safeloadGain(
|
|
||||||
static_cast<unsigned>(ADDR_STMIXER1_ST1_VOLUME), mixer.mixRight);
|
|
||||||
!result)
|
|
||||||
{
|
|
||||||
ESP_LOGW(kTag, "applyMixer: st1 safeload failed");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::expected<void, Adau1701Error> Adau1701Driver::setEqBand(
|
std::expected<void, Adau1701Error> Adau1701Driver::setEqBand(
|
||||||
core::EqBandIndex band, core::GainDb gain, core::FrequencyHz center, float q)
|
core::EqBandIndex band, core::GainDb gain, core::FrequencyHz center, float q)
|
||||||
{
|
{
|
||||||
@@ -487,6 +454,103 @@ namespace adau1701
|
|||||||
return safeloadFixpoint(static_cast<unsigned>(ADDR_BEEP1_KICK), value);
|
return safeloadFixpoint(static_cast<unsigned>(ADDR_BEEP1_KICK), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Compiled SigmaStudio defaults for Bass Boost1's crossover filter
|
||||||
|
// and 33-point compander curve (DigiRadioFinale_IC_1_PARAM.h,
|
||||||
|
// 2026-08-25 export). setBassBoostLevel() interpolates from these
|
||||||
|
// toward an identity/unity bypass as level goes from 100 to 0 --
|
||||||
|
// ADI's Dynamic Bass Boost algorithm internals aren't documented,
|
||||||
|
// so this is a linear blend of the known-good compiled curve, not
|
||||||
|
// a re-derivation of the algorithm's own math.
|
||||||
|
constexpr float kBassBoostB0 = 4.24433093514364e-05F;
|
||||||
|
constexpr float kBassBoostB1 = 8.48866187028728e-05F;
|
||||||
|
constexpr float kBassBoostB2 = 4.24433093514364e-05F;
|
||||||
|
constexpr float kBassBoostA1 = 1.98148576456209F;
|
||||||
|
constexpr float kBassBoostA2 = -0.981655537799498F;
|
||||||
|
constexpr float kBassBoostTable[33] = {
|
||||||
|
0.319153785510076F, 0.319153785510076F, 0.319153785510076F,
|
||||||
|
0.319153785510076F, 0.319153785510076F, 0.319153785510076F,
|
||||||
|
0.319153785510076F, 0.319153785510076F, 0.319153785510076F,
|
||||||
|
0.319153785510076F, 0.319153785510076F, 0.319153785510076F,
|
||||||
|
0.319153785510076F, 0.319153785510076F, 0.319153785510076F,
|
||||||
|
0.319153785510076F, 0.319153785510076F, 0.319153785510076F,
|
||||||
|
0.319153785510076F, 0.319153785510076F, 0.319153785510076F,
|
||||||
|
0.319153785510076F, 0.319153785510076F, 0.319153785510076F,
|
||||||
|
0.322849412171264F, 0.334195040026114F, 0.345939377826122F,
|
||||||
|
0.358096437102636F, 0.370680721782576F, 0.383707245492279F,
|
||||||
|
0.39719154946944F, 0.411149721104522F, 0.425598413133743F,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr float kSpreadSpread1 = 0.0629093159447337F;
|
||||||
|
constexpr float kSpreadSpread2 = 0.0193038143874401F;
|
||||||
|
|
||||||
|
[[nodiscard]] float towardIdentity(float compiled, float identity,
|
||||||
|
float level) noexcept
|
||||||
|
{
|
||||||
|
return identity + (compiled - identity) * level;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::expected<void, Adau1701Error> Adau1701Driver::setBassBoostLevel(
|
||||||
|
core::EnhanceLevel level)
|
||||||
|
{
|
||||||
|
if (auto ready = ensureBooted(); !ready)
|
||||||
|
{
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
const float t = level.fraction();
|
||||||
|
unsigned addrs[5U];
|
||||||
|
int values[5U];
|
||||||
|
addrs[0] = static_cast<unsigned>(ADDR_BASSBOOST1_B0);
|
||||||
|
addrs[1] = static_cast<unsigned>(ADDR_BASSBOOST1_B1);
|
||||||
|
addrs[2] = static_cast<unsigned>(ADDR_BASSBOOST1_B2);
|
||||||
|
addrs[3] = static_cast<unsigned>(ADDR_BASSBOOST1_A1);
|
||||||
|
addrs[4] = static_cast<unsigned>(ADDR_BASSBOOST1_A2);
|
||||||
|
values[0] = core::floatToFixpoint823(towardIdentity(kBassBoostB0, 1.0F, t));
|
||||||
|
values[1] = core::floatToFixpoint823(towardIdentity(kBassBoostB1, 0.0F, t));
|
||||||
|
values[2] = core::floatToFixpoint823(towardIdentity(kBassBoostB2, 0.0F, t));
|
||||||
|
values[3] = core::floatToFixpoint823(towardIdentity(kBassBoostA1, 0.0F, t));
|
||||||
|
values[4] = core::floatToFixpoint823(towardIdentity(kBassBoostA2, 0.0F, t));
|
||||||
|
if (sigma_safeload_block(5U, addrs, values) != 0)
|
||||||
|
{
|
||||||
|
return std::unexpected(Adau1701Error::SafeloadFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0U; i < 33U; ++i)
|
||||||
|
{
|
||||||
|
const unsigned addr =
|
||||||
|
static_cast<unsigned>(ADDR_BASSBOOST1_TABLE0) + i;
|
||||||
|
const std::int32_t value = core::floatToFixpoint823(
|
||||||
|
towardIdentity(kBassBoostTable[i], 1.0F, t));
|
||||||
|
if (auto result = safeloadFixpoint(addr, value); !result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, Adau1701Error> Adau1701Driver::setStereoSpreadLevel(
|
||||||
|
core::EnhanceLevel level)
|
||||||
|
{
|
||||||
|
if (auto ready = ensureBooted(); !ready)
|
||||||
|
{
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
const float t = level.fraction();
|
||||||
|
if (auto result = safeloadFixpoint(
|
||||||
|
static_cast<unsigned>(ADDR_SPHAT1_SPREAD1),
|
||||||
|
core::floatToFixpoint823(kSpreadSpread1 * t));
|
||||||
|
!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return safeloadFixpoint(
|
||||||
|
static_cast<unsigned>(ADDR_SPHAT1_SPREAD2),
|
||||||
|
core::floatToFixpoint823(kSpreadSpread2 * t));
|
||||||
|
}
|
||||||
|
|
||||||
std::expected<void, Adau1701Error> Adau1701Driver::writeRawParam(
|
std::expected<void, Adau1701Error> Adau1701Driver::writeRawParam(
|
||||||
unsigned address, float value)
|
unsigned address, float value)
|
||||||
{
|
{
|
||||||
@@ -497,6 +561,121 @@ namespace adau1701
|
|||||||
return safeloadFixpoint(address, core::floatToFixpoint823(value));
|
return safeloadFixpoint(address, core::floatToFixpoint823(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// ADAU1701 Data Capture Register, address 2074 (0x081A) -- see
|
||||||
|
// datasheet Rev.0 "2074 TO 2075 (0x081A TO 0x081B)--DATA CAPTURE
|
||||||
|
// REGISTERS" (p.36) and Table 32's register-map bit layout (p.30):
|
||||||
|
// D[11:2]=PC[9:0] (program-step index), D[1:0]=RS[1:0] (register
|
||||||
|
// select). Reading the same address afterward returns a 3-byte,
|
||||||
|
// 24-bit two's-complement 5.19 value (5.23 with the 4 LSBs
|
||||||
|
// truncated, per the datasheet). The per-meter (progCount, regSel)
|
||||||
|
// pairs below are read verbatim from SigmaStudio's own compiler
|
||||||
|
// output (Firmware/ADAU1701-Firmware/IC 1_DigiRadioFinale/
|
||||||
|
// net_list_out2/trap.dat), not invented.
|
||||||
|
constexpr unsigned kDataCaptureAddr = 0x081AU;
|
||||||
|
constexpr unsigned kRegSelMacOut = 2U; // "mult_out" in trap.dat
|
||||||
|
|
||||||
|
struct MeterPoint
|
||||||
|
{
|
||||||
|
unsigned progCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr MeterPoint kRadioInLeft{221}; // SingleBandLevelDet1
|
||||||
|
constexpr MeterPoint kRadioInRight{254}; // SingleBandLevelDet2
|
||||||
|
constexpr MeterPoint kBluetoothInLeft{155}; // SingleBandLevelDet3
|
||||||
|
constexpr MeterPoint kBluetoothInRight{188}; // SingleBandLevelDet4
|
||||||
|
constexpr MeterPoint kOutputLeft{717}; // SingleBandLevelDet5
|
||||||
|
constexpr MeterPoint kOutputRight{684}; // SingleBandLevelDet6
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
std::expected<float, Adau1701Error> Adau1701Driver::readCaptureDb(
|
||||||
|
unsigned progCount, unsigned regSel)
|
||||||
|
{
|
||||||
|
const unsigned char deviceAddr =
|
||||||
|
static_cast<unsigned char>(pins_.i2cAddr7 << 1);
|
||||||
|
const std::uint16_t config = static_cast<std::uint16_t>(
|
||||||
|
((progCount & 0x3FFU) << 2) | (regSel & 0x3U));
|
||||||
|
unsigned char configBytes[2U] = {
|
||||||
|
static_cast<unsigned char>((config >> 8) & 0xFFU),
|
||||||
|
static_cast<unsigned char>(config & 0xFFU),
|
||||||
|
};
|
||||||
|
if (SIGMA_WRITE_REGISTER_BLOCK(deviceAddr, kDataCaptureAddr, 2U,
|
||||||
|
configBytes) != 0)
|
||||||
|
{
|
||||||
|
return std::unexpected(Adau1701Error::SafeloadFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char raw[3U] = {0U, 0U, 0U};
|
||||||
|
if (sigma_i2c_read(kDataCaptureAddr, raw, sizeof(raw)) != 0)
|
||||||
|
{
|
||||||
|
return std::unexpected(Adau1701Error::SafeloadFailed);
|
||||||
|
}
|
||||||
|
std::int32_t value = (static_cast<std::int32_t>(raw[0]) << 16)
|
||||||
|
| (static_cast<std::int32_t>(raw[1]) << 8)
|
||||||
|
| static_cast<std::int32_t>(raw[2]);
|
||||||
|
if ((value & (1 << 23)) != 0)
|
||||||
|
{
|
||||||
|
value -= (1 << 24);
|
||||||
|
}
|
||||||
|
const float linear =
|
||||||
|
static_cast<float>(value) / static_cast<float>(1 << 19);
|
||||||
|
constexpr float kFloorDb = -96.0F;
|
||||||
|
if (std::fabs(linear) < 1e-5F)
|
||||||
|
{
|
||||||
|
return kFloorDb;
|
||||||
|
}
|
||||||
|
const float db = 20.0F * std::log10(std::fabs(linear));
|
||||||
|
return db < kFloorDb ? kFloorDb : db;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<core::AudioLevels, Adau1701Error> Adau1701Driver::readLevels()
|
||||||
|
{
|
||||||
|
if (auto ready = ensureBooted(); !ready)
|
||||||
|
{
|
||||||
|
return std::unexpected(ready.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
core::AudioLevels levels{};
|
||||||
|
auto read = [this](MeterPoint point,
|
||||||
|
float &out) -> std::expected<void, Adau1701Error>
|
||||||
|
{
|
||||||
|
const auto db = readCaptureDb(point.progCount, kRegSelMacOut);
|
||||||
|
if (!db)
|
||||||
|
{
|
||||||
|
return std::unexpected(db.error());
|
||||||
|
}
|
||||||
|
out = *db;
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto r = read(kRadioInLeft, levels.radioInLeftDb); !r)
|
||||||
|
{
|
||||||
|
return std::unexpected(r.error());
|
||||||
|
}
|
||||||
|
if (auto r = read(kRadioInRight, levels.radioInRightDb); !r)
|
||||||
|
{
|
||||||
|
return std::unexpected(r.error());
|
||||||
|
}
|
||||||
|
if (auto r = read(kBluetoothInLeft, levels.bluetoothInLeftDb); !r)
|
||||||
|
{
|
||||||
|
return std::unexpected(r.error());
|
||||||
|
}
|
||||||
|
if (auto r = read(kBluetoothInRight, levels.bluetoothInRightDb); !r)
|
||||||
|
{
|
||||||
|
return std::unexpected(r.error());
|
||||||
|
}
|
||||||
|
if (auto r = read(kOutputLeft, levels.outputLeftDb); !r)
|
||||||
|
{
|
||||||
|
return std::unexpected(r.error());
|
||||||
|
}
|
||||||
|
if (auto r = read(kOutputRight, levels.outputRightDb); !r)
|
||||||
|
{
|
||||||
|
return std::unexpected(r.error());
|
||||||
|
}
|
||||||
|
return levels;
|
||||||
|
}
|
||||||
|
|
||||||
std::expected<void, Adau1701Error> Adau1701Driver::applyEq(
|
std::expected<void, Adau1701Error> Adau1701Driver::applyEq(
|
||||||
const core::EqProfile &eq)
|
const core::EqProfile &eq)
|
||||||
{
|
{
|
||||||
@@ -535,7 +714,7 @@ namespace adau1701
|
|||||||
{
|
{
|
||||||
return ready;
|
return ready;
|
||||||
}
|
}
|
||||||
if (auto result = applyMixer(profile.mixer); !result)
|
if (auto result = selectSource(profile.activeSource); !result)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -543,7 +722,17 @@ namespace adau1701
|
|||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return setMasterVolume(profile.masterLeft, profile.masterRight);
|
if (auto result = setMasterVolume(profile.masterLeft, profile.masterRight);
|
||||||
|
!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (auto result = setBassBoostLevel(profile.enhancements.bass);
|
||||||
|
!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return setStereoSpreadLevel(profile.enhancements.stereo);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace adau1701
|
} // namespace adau1701
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ std::expected<void, core::DspError> Adau1701Dsp::applyProfile(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, core::DspError> Adau1701Dsp::applyMixer(
|
std::expected<void, core::DspError> Adau1701Dsp::selectSource(
|
||||||
const core::MixerState& mixer)
|
core::ActiveSource source)
|
||||||
{
|
{
|
||||||
if (auto result = driver_.applyMixer(mixer); !result) {
|
if (auto result = driver_.selectSource(source); !result) {
|
||||||
return std::unexpected(mapError(result.error()));
|
return std::unexpected(mapError(result.error()));
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
@@ -61,15 +61,6 @@ std::expected<void, core::DspError> Adau1701Dsp::applyEq(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, core::DspError> Adau1701Dsp::setInputVolume(
|
|
||||||
core::MixSource source, core::GainDb left, core::GainDb right)
|
|
||||||
{
|
|
||||||
if (auto result = driver_.setInputVolume(source, left, right); !result) {
|
|
||||||
return std::unexpected(mapError(result.error()));
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::expected<void, core::DspError> Adau1701Dsp::setMasterVolume(
|
std::expected<void, core::DspError> Adau1701Dsp::setMasterVolume(
|
||||||
core::GainDb left, core::GainDb right)
|
core::GainDb left, core::GainDb right)
|
||||||
{
|
{
|
||||||
@@ -97,6 +88,24 @@ std::expected<void, core::DspError> Adau1701Dsp::setBeepEnabled(bool enabled)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::expected<void, core::DspError> Adau1701Dsp::setBassBoostLevel(
|
||||||
|
core::EnhanceLevel level)
|
||||||
|
{
|
||||||
|
if (auto result = driver_.setBassBoostLevel(level); !result) {
|
||||||
|
return std::unexpected(mapError(result.error()));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<void, core::DspError> Adau1701Dsp::setStereoSpreadLevel(
|
||||||
|
core::EnhanceLevel level)
|
||||||
|
{
|
||||||
|
if (auto result = driver_.setStereoSpreadLevel(level); !result) {
|
||||||
|
return std::unexpected(mapError(result.error()));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::expected<void, core::DspError> Adau1701Dsp::writeRawParam(
|
std::expected<void, core::DspError> Adau1701Dsp::writeRawParam(
|
||||||
unsigned address, float value)
|
unsigned address, float value)
|
||||||
{
|
{
|
||||||
@@ -106,4 +115,13 @@ std::expected<void, core::DspError> Adau1701Dsp::writeRawParam(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::expected<core::AudioLevels, core::DspError> Adau1701Dsp::readLevels()
|
||||||
|
{
|
||||||
|
auto result = driver_.readLevels();
|
||||||
|
if (!result) {
|
||||||
|
return std::unexpected(mapError(result.error()));
|
||||||
|
}
|
||||||
|
return *result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace adau1701
|
} // namespace adau1701
|
||||||
|
|||||||
@@ -971,6 +971,39 @@ esp_err_t audioBeepPostHandler(httpd_req_t* req)
|
|||||||
return httpd_resp_send(req, json.c_str(), json.size());
|
return httpd_resp_send(req, json.c_str(), json.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief audioLevelsGetHandler — serve GET /api/audio/levels as JSON.
|
||||||
|
*
|
||||||
|
* @dname audioLevelsGetHandler
|
||||||
|
* @param req HTTP request handle from esp_http_server.
|
||||||
|
* @return ESP_OK on success, or an esp_err_t error code.
|
||||||
|
* @pubstate Reads all six 1×RTA level meters live on every call -- no
|
||||||
|
* background polling, no caching.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
esp_err_t audioLevelsGetHandler(httpd_req_t* req)
|
||||||
|
{
|
||||||
|
auto* ctx = routeContextFrom(req);
|
||||||
|
if (ctx == nullptr || ctx->audio == nullptr) {
|
||||||
|
httpd_resp_set_status(req, "503 Service Unavailable");
|
||||||
|
return httpd_resp_send(req, nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto levels = ctx->audio->readLevels();
|
||||||
|
if (!levels) {
|
||||||
|
const std::string json = core::serializeAudioErrorJson("dsp_failed");
|
||||||
|
httpd_resp_set_status(req, "500 Internal Server Error");
|
||||||
|
httpd_resp_set_type(req, "application/json");
|
||||||
|
return httpd_resp_send(req, json.c_str(), json.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string json = core::serializeAudioLevelsJson(*levels);
|
||||||
|
httpd_resp_set_type(req, "application/json");
|
||||||
|
return httpd_resp_send(req, json.c_str(), json.size());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief dspParamsGetHandler — serve GET /api/dsp/params as JSON.
|
* @brief dspParamsGetHandler — serve GET /api/dsp/params as JSON.
|
||||||
*
|
*
|
||||||
@@ -2252,6 +2285,14 @@ std::expected<void, NetError> SetupWebServer::start(
|
|||||||
};
|
};
|
||||||
httpd_register_uri_handler(server_, &audioBeepUri);
|
httpd_register_uri_handler(server_, &audioBeepUri);
|
||||||
|
|
||||||
|
const httpd_uri_t audioLevelsUri = {
|
||||||
|
.uri = "/api/audio/levels",
|
||||||
|
.method = HTTP_GET,
|
||||||
|
.handler = audioLevelsGetHandler,
|
||||||
|
.user_ctx = routeCtx,
|
||||||
|
};
|
||||||
|
httpd_register_uri_handler(server_, &audioLevelsUri);
|
||||||
|
|
||||||
const httpd_uri_t dspParamsUri = {
|
const httpd_uri_t dspParamsUri = {
|
||||||
.uri = "/api/dsp/params",
|
.uri = "/api/dsp/params",
|
||||||
.method = HTTP_GET,
|
.method = HTTP_GET,
|
||||||
|
|||||||
@@ -12,16 +12,16 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/ActiveSource.hpp"
|
||||||
|
#include "core/AudioLevels.hpp"
|
||||||
#include "core/AudioProfile.hpp"
|
#include "core/AudioProfile.hpp"
|
||||||
#include "core/DspError.hpp"
|
#include "core/DspError.hpp"
|
||||||
#include "core/EnhanceLevel.hpp"
|
#include "core/EnhanceLevel.hpp"
|
||||||
#include "core/EnhancementsDesign.hpp"
|
|
||||||
#include "core/EqBandIndex.hpp"
|
#include "core/EqBandIndex.hpp"
|
||||||
#include "core/FrequencyHz.hpp"
|
#include "core/FrequencyHz.hpp"
|
||||||
#include "core/GainDb.hpp"
|
#include "core/GainDb.hpp"
|
||||||
#include "core/IAudioProfileStore.hpp"
|
#include "core/IAudioProfileStore.hpp"
|
||||||
#include "core/IDsp.hpp"
|
#include "core/IDsp.hpp"
|
||||||
#include "core/MixSource.hpp"
|
|
||||||
#include "core/StoreError.hpp"
|
#include "core/StoreError.hpp"
|
||||||
|
|
||||||
#include <expected>
|
#include <expected>
|
||||||
@@ -106,22 +106,19 @@ public:
|
|||||||
const core::AudioProfile& profile, bool persist);
|
const core::AudioProfile& profile, bool persist);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief setInputVolume — update one input path and apply live.
|
* @brief selectSource — switch MX1's active input pair and apply live.
|
||||||
*
|
*
|
||||||
* @dname setInputVolume
|
* @dname selectSource
|
||||||
* @param source Si4684 or ESP32 path.
|
* @param source Which stereo pair MX1 should pass through.
|
||||||
* @param left Left gain.
|
|
||||||
* @param right Right gain.
|
|
||||||
* @param persist When true and store is set, write NVS.
|
* @param persist When true and store is set, write NVS.
|
||||||
* @return Ok on success, or StoreError wrapping DspError/IoFailed.
|
* @return Ok on success, or StoreError wrapping DspError/IoFailed.
|
||||||
* @pubstate updates profile_.mixer and safeloads.
|
* @pubstate updates profile_.activeSource and safeloads.
|
||||||
*
|
*
|
||||||
* @author Michele Bigi
|
* @author Michele Bigi
|
||||||
* @date 2026-07-06
|
* @date 2026-08-25
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::expected<void, core::StoreError> setInputVolume(
|
[[nodiscard]] std::expected<void, core::StoreError> selectSource(
|
||||||
core::MixSource source, core::GainDb left, core::GainDb right,
|
core::ActiveSource source, bool persist);
|
||||||
bool persist);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief setMasterVolume — update master output and apply live.
|
* @brief setMasterVolume — update master output and apply live.
|
||||||
@@ -233,15 +230,26 @@ public:
|
|||||||
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
[[nodiscard]] std::expected<void, core::DspError> writeRawParam(
|
||||||
unsigned address, float value);
|
unsigned address, float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief readLevels — on-demand read of all six level meters.
|
||||||
|
*
|
||||||
|
* @dname readLevels
|
||||||
|
* @return AudioLevels snapshot, or DspError.
|
||||||
|
* @pubstate live-only: not part of AudioProfile, never persisted, does
|
||||||
|
* not touch profile_.
|
||||||
|
*
|
||||||
|
* @author Michele Bigi
|
||||||
|
* @date 2026-08-25
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::expected<core::AudioLevels, core::DspError>
|
||||||
|
readLevels();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] std::expected<void, core::StoreError> persistProfile() const;
|
[[nodiscard]] std::expected<void, core::StoreError> persistProfile() const;
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::StoreError> applyProfileToDsp(
|
[[nodiscard]] std::expected<void, core::StoreError> applyProfileToDsp(
|
||||||
const core::AudioProfile& profile);
|
const core::AudioProfile& profile);
|
||||||
|
|
||||||
[[nodiscard]] std::expected<void, core::StoreError> applyEffectiveEq(
|
|
||||||
bool persist);
|
|
||||||
|
|
||||||
core::IDsp& dsp_;
|
core::IDsp& dsp_;
|
||||||
core::IAudioProfileStore* store_;
|
core::IAudioProfileStore* store_;
|
||||||
core::AudioProfile profile_;
|
core::AudioProfile profile_;
|
||||||
|
|||||||
@@ -21,15 +21,6 @@ namespace {
|
|||||||
|
|
||||||
constexpr char kTag[] = "AudioService";
|
constexpr char kTag[] = "AudioService";
|
||||||
|
|
||||||
[[nodiscard]] core::AudioProfile profileForHardware(
|
|
||||||
const core::AudioProfile& profile) noexcept
|
|
||||||
{
|
|
||||||
core::AudioProfile hardware = profile;
|
|
||||||
hardware.eq =
|
|
||||||
core::applyEnhancementsToEq(profile.eq, profile.enhancements);
|
|
||||||
return hardware;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AudioService::AudioService(core::IDsp& dsp, core::IAudioProfileStore* store)
|
AudioService::AudioService(core::IDsp& dsp, core::IAudioProfileStore* store)
|
||||||
@@ -81,28 +72,13 @@ std::expected<void, core::StoreError> AudioService::persistProfile() const
|
|||||||
std::expected<void, core::StoreError> AudioService::applyProfileToDsp(
|
std::expected<void, core::StoreError> AudioService::applyProfileToDsp(
|
||||||
const core::AudioProfile& profile)
|
const core::AudioProfile& profile)
|
||||||
{
|
{
|
||||||
const core::AudioProfile hardware = profileForHardware(profile);
|
if (auto applied = dsp_.applyProfile(profile); !applied) {
|
||||||
if (auto applied = dsp_.applyProfile(hardware); !applied) {
|
|
||||||
ESP_LOGW(kTag, "applyProfileToDsp: DSP safeload failed");
|
ESP_LOGW(kTag, "applyProfileToDsp: DSP safeload failed");
|
||||||
return std::unexpected(core::StoreError::IoFailed);
|
return std::unexpected(core::StoreError::IoFailed);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, core::StoreError> AudioService::applyEffectiveEq(
|
|
||||||
bool persist)
|
|
||||||
{
|
|
||||||
const core::EqProfile effective = core::applyEnhancementsToEq(
|
|
||||||
profile_.eq, profile_.enhancements);
|
|
||||||
if (auto applied = dsp_.applyEq(effective); !applied) {
|
|
||||||
return std::unexpected(core::StoreError::IoFailed);
|
|
||||||
}
|
|
||||||
if (persist) {
|
|
||||||
return persistProfile();
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::expected<void, core::StoreError> AudioService::applyProfile(
|
std::expected<void, core::StoreError> AudioService::applyProfile(
|
||||||
const core::AudioProfile& profile, bool persist)
|
const core::AudioProfile& profile, bool persist)
|
||||||
{
|
{
|
||||||
@@ -116,21 +92,13 @@ std::expected<void, core::StoreError> AudioService::applyProfile(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, core::StoreError> AudioService::setInputVolume(
|
std::expected<void, core::StoreError> AudioService::selectSource(
|
||||||
core::MixSource source, core::GainDb left, core::GainDb right, bool persist)
|
core::ActiveSource source, bool persist)
|
||||||
{
|
{
|
||||||
if (auto applied = dsp_.setInputVolume(source, left, right); !applied) {
|
if (auto applied = dsp_.selectSource(source); !applied) {
|
||||||
return std::unexpected(core::StoreError::IoFailed);
|
return std::unexpected(core::StoreError::IoFailed);
|
||||||
}
|
}
|
||||||
|
profile_.activeSource = source;
|
||||||
if (source == core::MixSource::Si4684) {
|
|
||||||
profile_.mixer.si4684Left = left;
|
|
||||||
profile_.mixer.si4684Right = right;
|
|
||||||
} else {
|
|
||||||
profile_.mixer.esp32Left = left;
|
|
||||||
profile_.mixer.esp32Right = right;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (persist) {
|
if (persist) {
|
||||||
return persistProfile();
|
return persistProfile();
|
||||||
}
|
}
|
||||||
@@ -155,10 +123,10 @@ std::expected<void, core::StoreError> AudioService::applyRadioFirstMix(
|
|||||||
bool persist)
|
bool persist)
|
||||||
{
|
{
|
||||||
const core::GainDb unity = core::GainDb::zero();
|
const core::GainDb unity = core::GainDb::zero();
|
||||||
profile_.mixer = core::MixerState::radioFirst();
|
profile_.activeSource = core::ActiveSource::Radio;
|
||||||
profile_.masterLeft = unity;
|
profile_.masterLeft = unity;
|
||||||
profile_.masterRight = unity;
|
profile_.masterRight = unity;
|
||||||
if (auto applied = dsp_.applyMixer(profile_.mixer); !applied) {
|
if (auto applied = dsp_.selectSource(profile_.activeSource); !applied) {
|
||||||
return std::unexpected(core::StoreError::IoFailed);
|
return std::unexpected(core::StoreError::IoFailed);
|
||||||
}
|
}
|
||||||
if (auto master = dsp_.setMasterVolume(unity, unity); !master) {
|
if (auto master = dsp_.setMasterVolume(unity, unity); !master) {
|
||||||
@@ -179,21 +147,39 @@ std::expected<void, core::StoreError> AudioService::setEqBand(
|
|||||||
.center = center,
|
.center = center,
|
||||||
.q = q,
|
.q = q,
|
||||||
});
|
});
|
||||||
return applyEffectiveEq(persist);
|
if (auto applied = dsp_.applyEq(profile_.eq); !applied) {
|
||||||
|
return std::unexpected(core::StoreError::IoFailed);
|
||||||
|
}
|
||||||
|
if (persist) {
|
||||||
|
return persistProfile();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, core::StoreError> AudioService::setStereoEnhance(
|
std::expected<void, core::StoreError> AudioService::setStereoEnhance(
|
||||||
core::EnhanceLevel level, bool persist)
|
core::EnhanceLevel level, bool persist)
|
||||||
{
|
{
|
||||||
|
if (auto applied = dsp_.setStereoSpreadLevel(level); !applied) {
|
||||||
|
return std::unexpected(core::StoreError::IoFailed);
|
||||||
|
}
|
||||||
profile_.enhancements.stereo = level;
|
profile_.enhancements.stereo = level;
|
||||||
return applyEffectiveEq(persist);
|
if (persist) {
|
||||||
|
return persistProfile();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, core::StoreError> AudioService::setBassEnhance(
|
std::expected<void, core::StoreError> AudioService::setBassEnhance(
|
||||||
core::EnhanceLevel level, bool persist)
|
core::EnhanceLevel level, bool persist)
|
||||||
{
|
{
|
||||||
|
if (auto applied = dsp_.setBassBoostLevel(level); !applied) {
|
||||||
|
return std::unexpected(core::StoreError::IoFailed);
|
||||||
|
}
|
||||||
profile_.enhancements.bass = level;
|
profile_.enhancements.bass = level;
|
||||||
return applyEffectiveEq(persist);
|
if (persist) {
|
||||||
|
return persistProfile();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::expected<void, core::DspError> AudioService::setBeepEnabled(bool enabled)
|
std::expected<void, core::DspError> AudioService::setBeepEnabled(bool enabled)
|
||||||
@@ -207,4 +193,9 @@ std::expected<void, core::DspError> AudioService::writeRawParam(
|
|||||||
return dsp_.writeRawParam(address, value);
|
return dsp_.writeRawParam(address, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::expected<core::AudioLevels, core::DspError> AudioService::readLevels()
|
||||||
|
{
|
||||||
|
return dsp_.readLevels();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace audio
|
} // namespace audio
|
||||||
|
|||||||
@@ -6,188 +6,210 @@ con il driver firmware (`components/drivers/adau1701/`) e le API HTTP
|
|||||||
(`components/net/src/SetupWebServer.cpp`). Ogni valore di indirizzo/registro citato è
|
(`components/net/src/SetupWebServer.cpp`). Ogni valore di indirizzo/registro citato è
|
||||||
letto direttamente dai file di export SigmaStudio, non inventato.
|
letto direttamente dai file di export SigmaStudio, non inventato.
|
||||||
|
|
||||||
**Root cause del bug "volume/mixer/EQ non si salvano mai" trovato e risolto in questa
|
**Questo documento descrive la revisione "DigiRadioFinale" (2026-08-25)**, che
|
||||||
sessione (2026-08-24): vedi §7.** Tutto il resto del documento descrive l'architettura
|
sostituisce il programma DSP precedente (74 parametri, mixer additivo) con uno
|
||||||
del programma DSP com'è oggi, dopo il fix.
|
molto più ricco (224 parametri) basato su un **selettore di sorgente esclusivo**
|
||||||
|
invece di un mixer. §5 raccoglie anche i bug storici della revisione precedente,
|
||||||
|
per riferimento.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Catena del segnale (da `DigiRadio_NetList.xml`)
|
## 1. Catena del segnale (da `DigiRadio_NetList.xml`)
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────┐
|
Input1 (Si4684 L/R, ESP32 L/R) ──┬─▶ DCB1 (L radio) ─┐
|
||||||
Si4684 (I2S) ──L──▶ │ Gain Si4674 │──▶┐
|
├─▶ DCB2 (R radio) ─┼─▶ Compressor1 ─▶ 1×RTA1 (L) ─┐
|
||||||
──R──▶ │ (addr 3,4) │──▶┤
|
├─▶ DCB4 (L esp32) ─┤ (stereo RMS) 1×RTA2 (R) ─┤
|
||||||
└─────────────┘ │
|
└─▶ DCB3 (R esp32) ─┘ 1×RTA3 (L) ─┤ (readback,
|
||||||
│ ┌──────────────┐ ┌──────────────┐
|
1×RTA4 (R) ─┤ ingresso)
|
||||||
ESP32 (I2S) ──L──▶ ┌─────────────┐ ├──▶│ St Mixer1 │──▶│ Param EQ1 │──┐
|
Beep1 ─▶ S Splitter1 (mono→stereo) ─────────────────────────────────────────────────┤
|
||||||
──R──▶ │ Gain ESP32 │──▶┤ │ ST0/ST1/ST2 │ │ 6 bande │ │
|
|
||||||
│ (addr 5,6) │──▶┘ │ (addr 9-11) │ │ (addr 12-41) │ │
|
|
||||||
└─────────────┘ └──────────────┘ └──────────────┘ │
|
|
||||||
│
|
|
||||||
Beep1 (interno) ──▶ Single1 ──▶ SSplitter1 ──▶ (ST2, ingresso mixer) │
|
|
||||||
(addr 0-2) (addr 7) (addr 8) │
|
|
||||||
▼
|
▼
|
||||||
┌──────────────────┐
|
┌──────────────────┐
|
||||||
│ Multiple1 (L/R) │
|
│ MX1 (mux) │
|
||||||
│ = MASTER VOLUME │
|
│ select = DC1 │
|
||||||
│ (addr 42, 43) │
|
│ 0=Radio 1=BT │
|
||||||
|
│ 2=Beep (raw int!) │
|
||||||
└──────────────────┘
|
└──────────────────┘
|
||||||
│
|
│
|
||||||
┌────────────┴────────────┐
|
▼
|
||||||
|
Param EQ1 (6 bande, addr 59-88)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
SPhat1 (Spatializer, addr 89-140)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Gen Filter1 (Voice Clarifier,
|
||||||
|
addr 141-145 — non tarato)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Multiple1 (Master Volume,
|
||||||
|
addr 146-147)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
Bass Boost1 (Dynamic Bass Boost,
|
||||||
|
addr 148-187)
|
||||||
|
│
|
||||||
|
┌──────────┴──────────┐
|
||||||
▼ ▼
|
▼ ▼
|
||||||
┌───────────────┐ ┌───────────────┐
|
1×RTA1_2 (readback) 1×RTA2_2 (readback)
|
||||||
│ Limiter1 (L) │ │ Limiter2 (R) │
|
│ │
|
||||||
│ (addr 44-58) │ │ (addr 59-73) │
|
▼ ▼
|
||||||
└───────────────┘ └───────────────┘
|
Limiter1 (L) Limiter2 (R)
|
||||||
|
addr 209-223 addr 194-208
|
||||||
│ │
|
│ │
|
||||||
▼ ▼
|
▼ ▼
|
||||||
Output1 Output2
|
Output1 Output2
|
||||||
(→ BT1035 I2S, L) (→ BT1035 I2S, R)
|
(→ BT1035 I2S, L) (→ BT1035 I2S, R)
|
||||||
```
|
```
|
||||||
|
|
||||||
Punto architetturale importante, spesso fonte di confusione: **ogni sorgente ha DUE
|
**Cambio architetturale rispetto alla revisione precedente**: non esiste più un
|
||||||
livelli di gain in cascata**, non uno solo:
|
mixer che combina Si4684 + ESP32 + Beep simultaneamente. `MX1` è un **selettore
|
||||||
|
esclusivo**: passa UNA SOLA delle tre coppie stereo alla volta, scelta scrivendo
|
||||||
|
`DC1` (indirizzo 3). Non esistono più gain pre-mixer per sorgente (`SI4674`/`ESP32`
|
||||||
|
sono spariti) — il livello si controlla solo con Master Volume ed EQ, a valle
|
||||||
|
della selezione.
|
||||||
|
|
||||||
1. **Gain pre-mixer** (`SI4674`/`SI4674_1` addr 3/4, `ESP32`/`ESP32_1` addr 5/6) — un
|
**Scoperta empirica cruciale (2026-08-25)**: `DC1` è dichiarato
|
||||||
gain indipendente per canale L e R, applicato PRIMA che il segnale entri nel mixer.
|
`SIGMASTUDIOTYPE_FIXPOINT` nell'export compilato (che farebbe pensare al formato
|
||||||
2. **Gain del mixer** (`STMIXER1_ST0_VOLUME`/`ST1_VOLUME` addr 9/10) — un SOLO gain per
|
5.23 usato da tutte le altre celle), ma **MX1 legge invece un intero a 32 bit
|
||||||
l'intera coppia stereo di quella sorgente, applicato AL mixer.
|
grezzo**. Scrivere `0x00800000` (= 1.0 in 5.23) non cambia la sorgente; scrivere
|
||||||
|
`0x00000001` sì. Confermato dal vivo: 0=Radio, 1=Bluetooth(ESP32), 2=Beep. Vedi
|
||||||
Se uno dei due è a -96dB (muto) e l'altro è aperto, il risultato finale è comunque
|
`adau1701::paramSourceIndex()` e `Adau1701Driver::selectSource()`.
|
||||||
muto — impostare solo uno dei due senza sapere dell'altro è la causa più comune di
|
|
||||||
"ho cambiato il volume ma non sento niente".
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Mappa completa dei registri Parameter RAM (74 indirizzi, 0-73)
|
## 2. Mappa completa dei registri Parameter RAM (224 indirizzi, 0-223)
|
||||||
|
|
||||||
| Indirizzo | Nome SigmaStudio | Funzione | Gruppo |
|
| Indirizzi | Blocco | Funzione | Controllo runtime |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 0 | `BEEP1_ENABLE` | Abilita generatore toni interno | Diagnostica |
|
| 0-2 | `BEEP1_*` | Tono di test interno | `setBeepEnabled()` (live-only) |
|
||||||
| 1 | `BEEP1_KICK` | Trigger del generatore toni | Diagnostica |
|
| 3 | `DC1` | Selettore sorgente per MX1 — **intero grezzo**, non 5.23 | `selectSource()` |
|
||||||
| 2 | `BEEP1_BEEP_FREQ` | Frequenza del tono (fissa, mai cambiata a runtime) | Diagnostica |
|
| 4-7 | `DCB1-4_POLE` | DC blocker, uno per canale (radio L/R, esp32 L/R) | Fisso (compilato) |
|
||||||
| 3 | `SI4674` | Gain pre-mixer Si4684, canale L | Mixer |
|
| 8 | `SSPLITTER1` | Fan-out mono→stereo per Beep | Fisso |
|
||||||
| 4 | `SI4674_1` | Gain pre-mixer Si4684, canale R | Mixer |
|
| 9-46 | `COMPRESSOR1_*` | Compressore RMS stereo (curva a 34 punti) su radio L/R | Fisso |
|
||||||
| 5 | `ESP32` | Gain pre-mixer streaming ESP32, canale L | Mixer |
|
| 47-58 | `1XRTA3,4,1,2` | 4 VU-meter in ingresso (radio L/R, esp32 L/R) | Lettura via indirizzo speciale 2074 — **meccanismo non ancora implementato in firmware** |
|
||||||
| 6 | `ESP32_1` | Gain pre-mixer streaming ESP32, canale R | Mixer |
|
| 59-88 | `PARAMEQ1_ST0..5_*` | EQ parametrico, 6 bande × 5 coefficienti (B0,B1,B2,A0,A1) | `setEqBand()`/`applyEq()` |
|
||||||
| 7 | `SINGLE1` | Gain del tono Beep prima dello splitter | Diagnostica |
|
| 89-140 | `SPHAT1_*` | Spatializer (SuperPhat) — solo `SPREAD1`/`SPREAD2` (139,140) sono la manopola di intensità; il resto (filtro crossover, tabella compander) è tarato in SigmaStudio | `setStereoSpreadLevel()` — scala SPREAD1/2 proporzionalmente (0=nessuno spread aggiunto, 100=pieno) |
|
||||||
| 8 | `SSPLITTER1` | Splitter mono→stereo per il tono Beep | Diagnostica |
|
| 141-145 | `GENFILTER1_ST0_*` | Voice Clarifier — **ancora piatto/identità** in questo export (b0=1, resto 0), non tarato | Non ancora implementato |
|
||||||
| 9 | `STMIXER1_ST0_VOLUME` | Gain mixer, leg Si4684 (stereo, un solo controllo) | Mixer |
|
| 146-147 | `MULTIPLE1`, `MULTIPLE1_1` | Master Volume L/R | `setMasterVolume()` |
|
||||||
| 10 | `STMIXER1_ST1_VOLUME` | Gain mixer, leg ESP32 (stereo, un solo controllo) | Mixer |
|
| 148-187 | `BASSBOOST1_*` | Dynamic Bass Boost — `BASSFREQUENCY`/`TIMECONSTANT` fissi; filtro crossover (B0,B1,B2,A1,A2) e tabella compander a 33 punti (`TABLE0-32`) sono la parte "intensità" | `setBassBoostLevel()` — scala filtro+tabella verso l'identità/unità (0=bypass piatto, 100=pieno compilato) |
|
||||||
| 11 | `STMIXER1_ST2_VOLUME` | Gain mixer, leg Beep — **mai esposto da nessuna API curata**, fisso a 1.0 (unity) di fabbrica | Mixer (non controllato) |
|
| 188-193 | `1XRTA2_2,1_2` | 2 VU-meter in uscita (post Bass Boost, pre limiter) | Stesso meccanismo di lettura non ancora implementato |
|
||||||
| 12-16 | `PARAMEQ1_ST0_*` (B0,B1,B2,A0,A1) | Banda EQ 0 — **filtro passa-alto fisso, mai scritto da `applyEq()`** | EQ (banda bloccata) |
|
| 194-208 | `LIMITER2_*` | Limiter canale R (soglia indirizzo 205) | Fisso |
|
||||||
| 17-21 | `PARAMEQ1_ST1_*` | Banda EQ 1 (default 100 Hz) | EQ |
|
| 209-223 | `LIMITER1_*` | Limiter canale L (soglia indirizzo 220) | Fisso |
|
||||||
| 22-26 | `PARAMEQ1_ST2_*` | Banda EQ 2 (default 400 Hz) | EQ |
|
|
||||||
| 27-31 | `PARAMEQ1_ST3_*` | Banda EQ 3 (default 1000 Hz) | EQ |
|
|
||||||
| 32-36 | `PARAMEQ1_ST4_*` | Banda EQ 4 (default 3000 Hz) | EQ |
|
|
||||||
| 37-41 | `PARAMEQ1_ST5_*` | Banda EQ 5 (default 8000 Hz) | EQ |
|
|
||||||
| 42 | `MULTIPLE1` | **Volume master**, canale L | Master |
|
|
||||||
| 43 | `MULTIPLE1_1` | **Volume master**, canale R | Master |
|
|
||||||
| 44-58 | `LIMITER1_*` | Limiter canale L (soglia a indirizzo 55) | Limiter |
|
|
||||||
| 59-73 | `LIMITER2_*` | Limiter canale R (soglia a indirizzo 70) | Limiter |
|
|
||||||
|
|
||||||
Le 6 bande EQ condividono gli **stessi coefficienti per L e R** (il cell "Parametric
|
Formato dati: fixed-point **5.23** per quasi tutte le celle (28 bit significativi,
|
||||||
EQ - Double Precision" è stereo ma con un solo set di coefficienti per banda) — non
|
sign-extend da bit 27 — vedi `core::floatToFixpoint823()`), **eccetto `DC1`** che è
|
||||||
esiste un controllo EQ separato per canale.
|
un intero grezzo a 32 bit (vedi sopra).
|
||||||
|
|
||||||
Formato dati: fixed-point **5.23** (28 bit significativi, non 32 — un errore di
|
|
||||||
decodifica di questo formato durante l'indagine del 23/08 aveva fatto sembrare
|
|
||||||
instabile un filtro che in realtà era corretto; vedi `core::floatToFixpoint823()` /
|
|
||||||
sign-extend da bit 27 in `Adau1701Driver.cpp`).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Registri di controllo (Core Control block, 0x800-0x827)
|
## 3. API HTTP e corrispondenza con i registri
|
||||||
|
|
||||||
| Indirizzo | Nome | Uso nel nostro firmware |
|
| Endpoint | Metodo | Corpo | Cosa tocca | Persistenza | Testato dal vivo |
|
||||||
|---|---|---|
|
|
||||||
| 0x81C | Core Control Register | Scritto una sola volta al boot dal replay del programma compilato |
|
|
||||||
| 0x81E | Serial Output Control | Compilato, mai modificato a runtime |
|
|
||||||
| 0x81F | Serial Input Control (bit IBP/ILP) | **Storia**: un override IBP=1 qui era stato introdotto il 16/08 insieme a un fix non correlato di Si4684, mai validato da solo, e causa del fruscio multi-giorno risolto oggi (24/08). Rimosso — ora resta al default compilato (0x00) |
|
|
||||||
| 0x820/0x821 | MP Config 0/1 | Compilato, mai toccato |
|
|
||||||
| 0x822 | Analog Power-Down | Compilato, mai toccato |
|
|
||||||
| 0x826 | Analog Interface Register 2 | Contiene i bit IBIAS_ADJ/VREF_TRIM di trim analogico interno, mai toccati; è un registro diverso da quello dove viveva l'override IBP (0x81F) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. API HTTP e corrispondenza con i registri — tutte testate dal vivo il 24/08
|
|
||||||
|
|
||||||
| Endpoint | Metodo | Corpo | Registri toccati | Persistenza NVS | Testato |
|
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| `/api/audio/profile` | GET | — | legge tutto | — | ✅ |
|
| `/api/audio/profile` | GET/PUT | `active_source`, `master`, `eq[6]`, `enhancements` | `DC1`, EQ 59-88, Master 146-147, Bass Boost/Spatializer via enhancements | ✅ | ✅ |
|
||||||
| `/api/audio/profile` | PUT | mixer + master + eq[6] + enhancements | 3,4,5,6,9,10,12-41,42,43 | ✅ | ✅ (round-trip completo dopo riavvio) |
|
| `/api/audio/reset` | POST | — | `AudioProfile::factoryDefault()` (Radio, EQ piatto, enhancements 0) | ✅ | — |
|
||||||
| `/api/audio/reset` | POST | — | ripristina `AudioProfile::factoryDefault()` (mixer: **entrambe** le sorgenti aperte a 0dB, non radio-first) | ✅ | ✅ |
|
| `/api/audio/stereo-enhance` | POST | `{"level":0-100}` | `SPHAT1_SPREAD1/2` (scala proporzionale) | ✅ | ✅ (effetto soggettivo/sottile) |
|
||||||
| `/api/audio/stereo-enhance` | POST | `{"level":0-100}` | bande EQ 3,4,5 (sovrascrive) | ✅ | ✅ |
|
| `/api/audio/bass-enhance` | POST | `{"level":0-100}` | `BASSBOOST1_*` filtro+tabella (scala verso identità) | ✅ | ✅ (confermato su radio, non su tono fisso — l'algoritmo è dinamico, reagisce a contenuto con dinamica reale) |
|
||||||
| `/api/audio/bass-enhance` | POST | `{"level":0-100}` | bande EQ 1,2 (sovrascrive) | ✅ | ✅ |
|
| `/api/audio/beep` | POST | `{"enabled":true/false}` | `BEEP1_ENABLE/KICK` | ❌ (live-only, per design) | ✅ |
|
||||||
| `/api/audio/beep` | POST | `{"enabled":true/false}` | 0 (`BEEP1_ENABLE`), 1 (`BEEP1_KICK`) | ❌ (live-only, come da design) | ✅ |
|
| `/api/audio/levels` | GET | — | legge live i 6 VU-meter (Data Capture Register 2074) | — (nessuna cache/polling) | ✅ |
|
||||||
| `/api/dsp/params` | GET | — | elenco statico nome→indirizzo (74 celle) | — | ✅ |
|
| `/api/dsp/params` | GET | — | elenco statico nome→indirizzo (230 celle, incluse le 6 speciali readback) | — | ✅ |
|
||||||
| `/api/dsp/param` | PUT | `{"name":"...","value":<float>}` | qualunque cella per nome, incluse quelle **non** raggiungibili dalle API curate (es. `STMIXER1_ST2_VOLUME`) | ❌ (live-only, "escape hatch" come SigmaStudio Remote Connection) | ✅ (scritto e ripristinato `STMIXER1_ST2_VOLUME`) |
|
| `/api/dsp/param` | PUT | `{"name":"...","value":<float>}` | qualunque cella per nome — **eccetto `DC1`, che qui va scritto già come intero puro, non tramite `selectSource()`** | ❌ (live-only) | ✅ (sweep completo di 223/224 celle scrivibili, 0 errori) |
|
||||||
| `/api/tuner/xtal-calibrate` | POST | `{"ibias","ctun","xtal_freq_hz"}` | Si4684 POWER_UP (non ADAU1701) | ✅ (dal fix di stasera) | ✅ |
|
|
||||||
|
|
||||||
### Campo `"locked"` per banda EQ (aggiunto oggi)
|
`active_source` accetta `"radio"`, `"bluetooth"`, `"beep"`. Il vecchio campo
|
||||||
|
`"mixer"` (con `si4684_left_db`/`esp32_left_db`/`mix_left_db` ecc.) **non esiste
|
||||||
|
più** — non ha più senso dato che non c'è più un mixer da bilanciare.
|
||||||
|
|
||||||
`GET/PUT /api/audio/profile` ora include `"locked":true/false` per ciascuna delle 6
|
Il campo `"locked"` per banda EQ (introdotto il 24/08 quando bass/stereo enhance
|
||||||
bande in `eq[]`:
|
sovrascrivevano le bande EQ) **ora è sempre `false` tranne la banda 0** (passa-alto
|
||||||
- banda 0: **sempre `true`** — è il passa-alto fisso, qualsiasi valore scritto è
|
fisso): bass/stereo enhance non toccano più l'EQ, guidano direttamente Bass
|
||||||
cosmetico, non ha mai effetto udibile (`Adau1701Driver::applyEq()` la salta
|
Boost1/SPhat1.
|
||||||
esplicitamente).
|
|
||||||
- bande 1,2: `true` quando `bass_level > 0` — l'enhancement le sovrascrive con valori
|
|
||||||
calcolati da formula, un edit manuale in quel momento non è udibile.
|
|
||||||
- bande 3,4,5: `true` quando `stereo_level > 0`, stesso motivo.
|
|
||||||
|
|
||||||
Prima di questo fix il comportamento era identico (le bande erano già sovrascritte),
|
|
||||||
ma l'API non lo segnalava — sembrava che "il salvataggio non facesse nulla" quando in
|
|
||||||
realtà stava facendo esattamente quello che il DSP prevede, solo senza dirlo.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Bug trovati e stato
|
## 4. VU-meter (readback) — implementato 2026-08-25
|
||||||
|
|
||||||
|
Le 6 celle `1×RTA*` sono veri VU-meter (level detector) posizionati in 4 punti
|
||||||
|
d'ingresso e 2 punti d'uscita. Il loro valore NON sta al normale indirizzo
|
||||||
|
Parameter RAM di ciascuna — usano il **Data Capture Register** dell'ADAU1701
|
||||||
|
(indirizzo 2074/0x081A, datasheet Rev.0 pag. 30 e 36, Table 28/29/32/44/45):
|
||||||
|
|
||||||
|
1. Si scrive un word di 16 bit a 2074: bit[11:2] = indice del passo di
|
||||||
|
programma (`PC[9:0]`) dove campionare, bit[1:0] = quale registro interno
|
||||||
|
del core trasferire (`RS[1:0]`; `10`=MAC_out).
|
||||||
|
2. Si rilegge lo stesso indirizzo 2074: risponde con 3 byte (24 bit,
|
||||||
|
complemento a due, formato 5.19 — il 5.23 interno con i 4 LSB troncati).
|
||||||
|
|
||||||
|
Gli indici `PC[9:0]` per ciascuno dei 6 meter **non sono stati inventati**:
|
||||||
|
vengono letti testualmente dal file che il compilatore SigmaStudio genera
|
||||||
|
apposta per questo (`IC 1_DigiRadioFinale/net_list_out2/trap.dat`):
|
||||||
|
|
||||||
|
| Meter | Punto | Program-step (trap.dat) |
|
||||||
|
|---|---|---|
|
||||||
|
| `1×RTA1` | Radio L, post-compressore | 221 |
|
||||||
|
| `1×RTA2` | Radio R, post-compressore | 254 |
|
||||||
|
| `1×RTA3` | ESP32 L | 155 |
|
||||||
|
| `1×RTA4` | ESP32 R | 188 |
|
||||||
|
| `1×RTA1_2` | Uscita L (post Bass Boost1) | 717 |
|
||||||
|
| `1×RTA2_2` | Uscita R (post Bass Boost1) | 684 |
|
||||||
|
|
||||||
|
Il chip ha **solo 2 registri di cattura hardware** (2074, 2075); il firmware
|
||||||
|
ne usa uno solo, riconfigurandolo e rileggendolo in sequenza per i 6 punti —
|
||||||
|
**solo quando arriva una richiesta** `GET /api/audio/levels`, nessun polling
|
||||||
|
in background. Testato dal vivo: valori plausibili e distinti per ciascun
|
||||||
|
punto, e l'uscita scende coerentemente abbassando il master volume.
|
||||||
|
|
||||||
|
Vedi `Adau1701Driver::readLevels()`/`readCaptureDb()` per l'implementazione.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Bug trovati e stato (cronologia)
|
||||||
|
|
||||||
| # | Sintomo | Causa reale | Stato |
|
| # | Sintomo | Causa reale | Stato |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 1 | Mixer/volume/EQ salvati non sopravvivono MAI a un riavvio | **NVS inizializzato DOPO** `AudioService::loadAndApply()` in `app_main()` — ogni lettura falliva con `ESP_ERR_NVS_NOT_INITIALIZED` (0x1101), silenziosamente, da sempre | ✅ risolto oggi — riordinato boot in `main.cpp`, verificato dal vivo |
|
| 1 | Mixer/volume/EQ salvati non sopravvivono MAI a un riavvio (revisione precedente) | NVS inizializzato dopo `AudioService::loadAndApply()` in `app_main()` | ✅ risolto 2026-08-24 |
|
||||||
| 2 | Anche dopo il fix #1, mixer e master tornavano comunque al preset "radio" a ogni riavvio | `HardwareBootstrap::boot()` chiamava `applyRadioFirstMix()` **incondizionatamente** dopo il caricamento, sovrascrivendo qualunque valore appena ripristinato da NVS | ✅ risolto oggi — ora condizionale, si applica solo se non c'è un profilo salvato |
|
| 2 | Mixer e master tornavano al preset "radio" a ogni riavvio anche dopo il fix #1 | `HardwareBootstrap::boot()` chiamava `applyRadioFirstMix()` incondizionatamente | ✅ risolto 2026-08-24 |
|
||||||
| 3 | Modificare la banda EQ 0 (20 Hz) via web UI/app non ha mai effetto udibile | È il filtro passa-alto fisso del programma compilato, `applyEq()` la salta di proposito | ✅ ora segnalato via `"locked":true`, non più silenzioso |
|
| 3 | Qualsiasi modifica EQ/enhancement diversa da zero → silenzio totale (anche sul tono di test) | Segno mancante in `core::designPeakingEq()`: i coefficienti di retroazione RBJ (sottrattivi) venivano scritti tali e quali nei registri A0/A1 dell'ADAU1701 (additivi) — retroazione positiva invece che negativa, il filtro divergeva su un valore costante (DC, inaudibile) | ✅ risolto 2026-08-25 — vedi `components/core/src/BiquadDesign.cpp`, test di stabilità in `biquad_design_test.cpp` |
|
||||||
| 4 | Attivare bass/stereo enhancement sembra "cancellare" le modifiche manuali dell'EQ sulle bande coinvolte | `core::applyEnhancementsToEq()` sovrascrive bande 1-2 (bass) e 3-5 (stereo) con valori a formula quando il livello è > 0 | ✅ ora segnalato via `"locked":true` sulle bande coinvolte; il comportamento DSP resta invariato (per design) |
|
| 4 | Bridge TCP SigmaStudio (porta 8086): `accept()` in loop infinito su EBADF da ogni boot | `SigmaStudioTcpServer::stop()` cancellava incondizionatamente il singleton del socket attivo, anche quando chiamato sull'oggetto move-from (ogni boot ne crea uno) | ✅ risolto 2026-08-25 |
|
||||||
| 5 | Il leg Beep del mixer (`STMIXER1_ST2_VOLUME`, indirizzo 11) non è mai regolabile dalle API curate | Nessun bug — è un ingresso diagnostico interno, mai stato nei piani esporlo. Raggiungibile comunque via `/api/dsp/param` se serve per debug | Non è un bug, solo documentato |
|
| 5 | Nuovo netlist DigiRadioFinale: nessun mixer, solo `MX1` a selezione esclusiva | Non è un bug — cambio di design intenzionale, confermato dall'utente | Documentato, non un bug |
|
||||||
| 6 | Fruscio persistente su radio e streaming (multi-giorno) | Override IBP=1 su `SerialInputRegister` (0x81F), introdotto l'8/16 agosto insieme a un fix non correlato, mai validato da solo — condiviso da tutti gli ingressi seriali del chip | ✅ risolto ieri (24/08, prima parte della sessione) — vedi `docs/si4684-rf-investigation-report.md` |
|
| 6 | Scrivere `DC1` in formato 5.23 (es. 1.0 = 0x00800000) non cambia la sorgente selezionata | `MX1` legge `DC1` come intero grezzo, non fixpoint, nonostante `TYPE_DC1` compilato dica `SIGMASTUDIOTYPE_FIXPOINT` | ✅ risolto 2026-08-25 — confermato dal vivo (0/1/2 → Radio/BT/Beep) |
|
||||||
|
| 7 | Fruscio persistente su radio e streaming (multi-giorno, revisione precedente) | Override IBP=1 su `SerialInputRegister` (0x81F), mai validato in isolamento | ✅ risolto 2026-08-24 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Come testare manualmente (comandi usati stasera)
|
## 6. Come testare manualmente
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Stato attuale
|
# Stato attuale
|
||||||
curl http://192.168.1.62/api/audio/profile
|
curl http://192.168.1.62/api/audio/profile
|
||||||
|
|
||||||
# Scrivi un pattern distintivo (mixer + master + 5 bande EQ)
|
# Cambia sorgente
|
||||||
curl -X PUT http://192.168.1.62/api/audio/profile -d '{
|
curl -X PUT http://192.168.1.62/api/audio/profile -d '{
|
||||||
"mixer": {"si4684_left_db": 0, "si4684_right_db": 0,
|
"active_source": "bluetooth",
|
||||||
"esp32_left_db": -96, "esp32_right_db": -96,
|
"master": {"left_db": 0, "right_db": 0},
|
||||||
"mix_left_db": 0, "mix_right_db": -96},
|
|
||||||
"master": {"left_db": -4, "right_db": -4},
|
|
||||||
"eq": [
|
"eq": [
|
||||||
{"gain_db": 0, "center_hz": 20, "q": 1.414},
|
{"gain_db": 0, "center_hz": 20, "q": 1.414},
|
||||||
{"gain_db": 5, "center_hz": 100, "q": 1},
|
{"gain_db": 0, "center_hz": 100, "q": 1},
|
||||||
{"gain_db": -3, "center_hz": 400, "q": 1.2},
|
{"gain_db": 0, "center_hz": 400, "q": 1},
|
||||||
{"gain_db": 2, "center_hz": 1000, "q": 1},
|
{"gain_db": 0, "center_hz": 1000, "q": 1},
|
||||||
{"gain_db": -1, "center_hz": 3000, "q": 1},
|
{"gain_db": 0, "center_hz": 3000, "q": 1},
|
||||||
{"gain_db": 4, "center_hz": 8000, "q": 1}
|
{"gain_db": 0, "center_hz": 8000, "q": 1}
|
||||||
],
|
],
|
||||||
"enhancements": {"stereo_level": 0, "bass_level": 0}
|
"enhancements": {"stereo_level": 0, "bass_level": 0}
|
||||||
}'
|
}'
|
||||||
|
|
||||||
# Riavvia la scheda (fisicamente, o via reset seriale) e rileggi:
|
# Bass Boost / Spatializer
|
||||||
curl http://192.168.1.62/api/audio/profile
|
curl -X POST http://192.168.1.62/api/audio/bass-enhance -d '{"level":100}'
|
||||||
# → deve mostrare ESATTAMENTE gli stessi valori scritti sopra
|
curl -X POST http://192.168.1.62/api/audio/stereo-enhance -d '{"level":100}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Per leggere/scrivere una singola cella arbitraria (debug avanzato, non persistente):
|
Per leggere/scrivere una singola cella arbitraria (debug avanzato, non
|
||||||
|
persistente — **non usare per `DC1`**, che ha una semantica intero-grezzo
|
||||||
|
diversa da tutte le altre celle):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl http://192.168.1.62/api/dsp/params # elenco nome→indirizzo
|
curl http://192.168.1.62/api/dsp/params # elenco nome→indirizzo (230 celle)
|
||||||
curl -X PUT http://192.168.1.62/api/dsp/param -d '{"name":"STMIXER1_ST2_VOLUME","value":1.0}'
|
curl -X PUT http://192.168.1.62/api/dsp/param -d '{"name":"SPHAT1_SPREAD1","value":0.0629}'
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -196,12 +218,12 @@ curl -X PUT http://192.168.1.62/api/dsp/param -d '{"name":"STMIXER1_ST2_VOLUME",
|
|||||||
|
|
||||||
| File | Contenuto |
|
| File | Contenuto |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Firmware/ADAU1701-Firmware/DigiRadio_IC_1_PARAM.h` | Sorgente di verità per tutti gli indirizzi Parameter RAM |
|
| `Firmware/ADAU1701-Firmware/DigiRadio_IC_1_PARAM.h` | Sorgente di verità per tutti gli indirizzi Parameter RAM (export DigiRadioFinale) |
|
||||||
| `Firmware/ADAU1701-Firmware/DigiRadio_NetList.xml` | Topologia del segnale (chi è collegato a chi) |
|
| `Firmware/ADAU1701-Firmware/DigiRadio_NetList.xml` | Topologia del segnale (chi è collegato a chi) |
|
||||||
| `components/drivers/adau1701/include/adau1701/Adau1701ParamTable.hpp` | Tabella nome→indirizzo generata da PARAM.h, usata da `/api/dsp/param` |
|
| `components/core/include/core/ActiveSource.hpp` | Enum Radio/Bluetooth/Beep + nota sulla semantica intero-grezzo di DC1 |
|
||||||
| `components/drivers/adau1701/include/adau1701/Adau1701ParamMap.hpp` | Funzioni helper indirizzo-per-banda-EQ e indirizzo-per-sorgente-mixer |
|
| `components/drivers/adau1701/include/adau1701/Adau1701ParamTable.hpp` | Tabella nome→indirizzo generata da PARAM.h (230 celle) |
|
||||||
| `components/drivers/adau1701/src/Adau1701Driver.cpp` | Implementazione `applyMixer`/`applyEq`/`setMasterVolume`/`setBeepEnabled` |
|
| `components/drivers/adau1701/include/adau1701/Adau1701ParamMap.hpp` | `paramAddrEqBandBase()`, `paramSourceIndex()` |
|
||||||
| `components/services/audio/src/AudioService.cpp` | `loadAndApply()`, `applyRadioFirstMix()`, enhancement plumbing |
|
| `components/drivers/adau1701/src/Adau1701Driver.cpp` | `selectSource()`, `applyEq()`, `setMasterVolume()`, `setBassBoostLevel()`, `setStereoSpreadLevel()`, `setBeepEnabled()` |
|
||||||
| `components/core/src/EnhancementsDesign.cpp` | Formula di sovrascrittura EQ per bass/stereo enhancement |
|
| `components/core/src/BiquadDesign.cpp` | `designPeakingEq()` — fix del segno A0/A1 (2026-08-25) |
|
||||||
| `main/main.cpp` | Ordine di boot (`app_main()`) — sede del fix root-cause di oggi |
|
| `components/services/audio/src/AudioService.cpp` | `loadAndApply()`, `applyRadioFirstMix()`, `selectSource()`, enhancement plumbing |
|
||||||
| `main/hardware_bootstrap.cpp` | `HardwareBootstrap::boot()` — sede del secondo fix (radio-first condizionale) |
|
| `docs/manual/ch-sigmastudio.tex` §"Next revision: DigiRadioFinale" | Diagramma e tabella indirizzi per il manuale PDF |
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
# Brief per Cursor — allineamento app iOS al firmware DigiRadioFinale
|
||||||
|
|
||||||
|
Questo documento è vincolante: ogni punto marcato **DEVE** è un requisito, non
|
||||||
|
un suggerimento. Se qualcosa non è chiaro o sembra in conflitto con codice
|
||||||
|
esistente, chiedi prima di improvvisare una soluzione diversa — non inventare
|
||||||
|
comportamenti non specificati qui.
|
||||||
|
|
||||||
|
Dispositivo di test reale: `http://192.168.1.62` (mDNS `digiradio-CC4DB4.local`).
|
||||||
|
Verifica ogni funzionalità contro il dispositivo vero prima di considerarla
|
||||||
|
finita, non solo con dati mock.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Problemi riportati oggi dall'utente (da risolvere tutti)
|
||||||
|
|
||||||
|
1. L'interfaccia non è "in stile Apple" — troppo grezza/disorganizzata.
|
||||||
|
2. I nuovi controlli DSP (selettore sorgente, Bass Boost, Stereo Spread) non
|
||||||
|
sono stati implementati.
|
||||||
|
3. Non è possibile selezionare la sorgente di ingresso (radio/Bluetooth).
|
||||||
|
4. Il controllo del volume è sbagliato/inconsistente.
|
||||||
|
5. Le stazioni: uno scan completo produce una lista lunga sia FM che DAB, ma
|
||||||
|
aprendo il tab FM o il tab DAB separatamente dice "nessuna stazione" —
|
||||||
|
vanno unificate in un'unica lista.
|
||||||
|
|
||||||
|
Le sezioni seguenti danno il contratto dati esatto e i requisiti UI per
|
||||||
|
risolvere ciascuno di questi punti. Non è accettabile implementare solo una
|
||||||
|
parte e considerare il resto "per dopo" senza dirlo esplicitamente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Selettore sorgente (sostituisce il vecchio mixer) — OBBLIGATORIO
|
||||||
|
|
||||||
|
Il firmware non ha più un mixer che combina sorgenti. **DEVE** esserci un
|
||||||
|
controllo a scelta singola (es. `Picker` segmented, non due slider separati)
|
||||||
|
con tre opzioni: **Radio**, **Bluetooth**, e opzionalmente **Test tone**
|
||||||
|
(quest'ultimo solo in una sezione diagnostica, non nella UI principale).
|
||||||
|
|
||||||
|
Contratto API — `PUT /api/audio/profile`, corpo completo (tutti i campi sono
|
||||||
|
obbligatori nella richiesta, il firmware non li fa opzionali):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"active_source": "radio",
|
||||||
|
"master": {"left_db": 0, "right_db": 0},
|
||||||
|
"eq": [
|
||||||
|
{"gain_db": 0, "center_hz": 20, "q": 1.414},
|
||||||
|
{"gain_db": 0, "center_hz": 100, "q": 1},
|
||||||
|
{"gain_db": 0, "center_hz": 400, "q": 1},
|
||||||
|
{"gain_db": 0, "center_hz": 1000, "q": 1},
|
||||||
|
{"gain_db": 0, "center_hz": 3000, "q": 1},
|
||||||
|
{"gain_db": 0, "center_hz": 8000, "q": 1}
|
||||||
|
],
|
||||||
|
"enhancements": {"stereo_level": 0, "bass_level": 0}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`active_source` accetta **esattamente** le stringhe `"radio"`, `"bluetooth"`,
|
||||||
|
`"beep"` (minuscolo, invariato). Nessun altro valore.
|
||||||
|
|
||||||
|
`GET /api/audio/profile` restituisce lo stesso oggetto — usalo per
|
||||||
|
inizializzare il Picker all'avvio schermata, non assumere sempre "radio".
|
||||||
|
|
||||||
|
**Errore comune da evitare**: NON costruire il body facendo merge parziale di
|
||||||
|
un vecchio oggetto `"mixer"` — quel campo non esiste più, se lo mandi il
|
||||||
|
parser lo ignora e basta (non causa errore, ma non fa nulla).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Volume — contratto esatto
|
||||||
|
|
||||||
|
Il volume master è **nello stesso oggetto profilo**, campo `"master"`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"master": {"left_db": -6, "right_db": -6}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Range valido: **-96.0 .. +12.0** (dB). Valori fuori range vengono rifiutati
|
||||||
|
dal firmware (risposta di errore, non clamping silenzioso) — clampa lato
|
||||||
|
client PRIMA di inviare.
|
||||||
|
- **Non fermare lo slider a 0 dB** — il firmware supporta fino a **+12 dB** di
|
||||||
|
guadagno reale sul master. Se lo slider attuale si ferma a 0 e "sembra
|
||||||
|
basso", è un limite arbitrario della UI, non del firmware: estendi il range
|
||||||
|
fino a +12 dB.
|
||||||
|
- Normalmente L e R vanno impostati **uguali** con un unico slider "Volume"
|
||||||
|
(non serve un controllo stereo separato per il volume master, a meno che
|
||||||
|
non venga esplicitamente richiesto un bilanciamento L/R).
|
||||||
|
- Ogni `PUT /api/audio/profile` **sostituisce l'intero profilo** — se lo slider
|
||||||
|
del volume invia solo `{"master": {...}}` senza gli altri campi
|
||||||
|
(`active_source`, `eq`, `enhancements`), il firmware risponde con errore di
|
||||||
|
campo mancante. **DEVI sempre inviare l'oggetto completo**, leggendo prima lo
|
||||||
|
stato corrente con `GET /api/audio/profile` (o tenendolo in uno store locale
|
||||||
|
sempre sincronizzato) e poi cambiando solo il campo che l'utente ha toccato.
|
||||||
|
|
||||||
|
Questo "invia sempre tutto l'oggetto" è quasi certamente la causa del
|
||||||
|
"volume sbagliato": se il codice attuale manda solo il volume da solo, il
|
||||||
|
firmware lo rifiuta o (se altri campi arrivano con default sbagliati) resetta
|
||||||
|
sorgente/EQ/enhancement senza che l'utente l'abbia chiesto.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Bass Boost e Stereo Spread — nuovi controlli, OBBLIGATORI
|
||||||
|
|
||||||
|
Due slider 0-100, indipendenti da sorgente/volume/EQ:
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/audio/bass-enhance {"level": 0-100}
|
||||||
|
POST /api/audio/stereo-enhance {"level": 0-100}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Questi sono endpoint **a sé stanti**, non fanno parte del body di
|
||||||
|
`/api/audio/profile` per la scrittura (ma il loro stato corrente torna
|
||||||
|
dentro `GET /api/audio/profile` → `"enhancements": {"bass_level":.., "stereo_level":..}`).
|
||||||
|
- Bass Boost è un algoritmo **dinamico**: su un tono fisso di test non si
|
||||||
|
sente quasi nulla, è normale — non è un bug se durante un test con la
|
||||||
|
radio spenta sembra "non fare niente".
|
||||||
|
- Non esiste più il vecchio comportamento per cui alzare questi livelli
|
||||||
|
"bloccava" delle bande dell'equalizzatore manuale — l'EQ è ora sempre
|
||||||
|
indipendente. Il campo `"locked"` per banda nell'array `eq[]` (in
|
||||||
|
`GET /api/audio/profile`) è ora sempre `false` tranne la banda 0 (che è
|
||||||
|
sempre `true`, invariato da prima — è un passa-alto fisso, non toccarlo).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Lista stazioni unificata — OBBLIGATORIO
|
||||||
|
|
||||||
|
Il backend ha **un solo elenco stazioni**, non due:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/stations
|
||||||
|
```
|
||||||
|
|
||||||
|
restituisce un array dove ogni stazione ha un campo `"band"` che vale
|
||||||
|
`"fm"` oppure `"dab"`, più i campi specifici di banda (`fm_frequency_khz` per
|
||||||
|
FM; `dab_freq_index`, `dab_service_id`, `dab_component_id` per DAB).
|
||||||
|
|
||||||
|
**Il bug riportato oggi** (lo scan produce una lista lunga, ma il tab FM o il
|
||||||
|
tab DAB dicono "nessuna stazione") indica che l'app sta usando due sorgenti
|
||||||
|
dati diverse — una per la lista generale/scan e una diversa (vuota o rotta)
|
||||||
|
per i tab FM/DAB filtrati. **DEVI unificare**: un solo `StationListStore` (o
|
||||||
|
equivalente) alimentato da `GET /api/stations`, con i tab FM e DAB che sono
|
||||||
|
semplicemente **filtri client-side** (`station.band == .fm` / `.dab`) sulla
|
||||||
|
STESSA lista, non due chiamate/store separati.
|
||||||
|
|
||||||
|
UI richiesta: un'unica lista in stile Apple (`List` con sezioni, o una vista
|
||||||
|
tipo Impostazioni), non due implementazioni diverse per FM e DAB — stesso
|
||||||
|
componente di riga, stesso stile, filtrato per banda.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Stile UI — Apple, minimalista ma con una sezione grafica curata
|
||||||
|
|
||||||
|
- Componenti nativi SwiftUI: `Picker` segmented per la sorgente, `Slider` con
|
||||||
|
`.tint()` per volume/bass/stereo, `List`/`Form` in stile Impostazioni per le
|
||||||
|
stazioni e le opzioni tecniche. Niente controlli custom pesanti o griglie di
|
||||||
|
bottoni non standard.
|
||||||
|
- Organizza per tab/sezione logica, non tutto in una schermata:
|
||||||
|
- **Ascolto**: sorgente attiva, volume, stazione corrente.
|
||||||
|
- **Suono**: EQ 6 bande, Bass Boost, Stereo Spread.
|
||||||
|
- **Stazioni**: lista unificata FM+DAB (vedi §4), con scan.
|
||||||
|
- **Bluetooth**: pairing, dispositivo connesso.
|
||||||
|
- **Diagnostica**: tono di test, dettagli tecnici/versione firmware — non
|
||||||
|
mescolare con i controlli quotidiani.
|
||||||
|
- Una sezione "grafica" curata è benvenuta (es. una card "Now Playing" con
|
||||||
|
sfondo sfumato/blur, animazione leggera sul cambio sorgente). Ora **puoi**
|
||||||
|
agganciarla a dati reali di livello audio — vedi §7, l'endpoint VU-meter è
|
||||||
|
disponibile da oggi.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. VU-meter — nuovo, disponibile da oggi
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/audio/levels
|
||||||
|
```
|
||||||
|
|
||||||
|
Risposta:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"radio_in_left_db": -1.5,
|
||||||
|
"radio_in_right_db": -1.5,
|
||||||
|
"bluetooth_in_left_db": -0.9,
|
||||||
|
"bluetooth_in_right_db": -1.0,
|
||||||
|
"output_left_db": -1.9,
|
||||||
|
"output_right_db": -2.0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Valori reali in dBFS, letti dal vivo dal DSP **ad ogni chiamata** — il
|
||||||
|
firmware non fa polling in background né cache. Se vuoi un meter che si
|
||||||
|
aggiorna in tempo reale, il polling lo fai tu lato app (es. ogni 200-500ms
|
||||||
|
mentre la schermata è visibile) — **fermalo quando la schermata non è a
|
||||||
|
video**, per non generare traffico I2C continuo inutile sul dispositivo.
|
||||||
|
Questa è la sezione "grafica" giusta per barre VU vere (radio in / BT in /
|
||||||
|
uscita), non un'animazione finta.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Checklist di autoverifica prima di considerare il lavoro finito
|
||||||
|
|
||||||
|
- [ ] Cambiare sorgente da Radio a Bluetooth nell'app cambia davvero l'audio
|
||||||
|
sul dispositivo reale (non solo lo stato locale dell'app).
|
||||||
|
- [ ] Cambiare il volume aggiorna il volume reale e **non** resetta
|
||||||
|
accidentalmente sorgente/EQ/enhancement.
|
||||||
|
- [ ] Bass Boost e Stereo Spread hanno uno slider visibile, funzionante, e il
|
||||||
|
valore torna corretto dopo un refresh/riavvio app (persistente lato
|
||||||
|
firmware).
|
||||||
|
- [ ] Il tab FM mostra le stazioni FM dello scan; il tab DAB mostra quelle
|
||||||
|
DAB; entrambe vengono dalla stessa fonte dati.
|
||||||
|
- [ ] Nessuna schermata mostra contemporaneamente controlli tecnici
|
||||||
|
(indirizzi DSP, tono di test) mescolati a quelli quotidiani.
|
||||||
|
- [ ] Lo slider del volume arriva fino a +12 dB, non si ferma a 0 dB.
|
||||||
|
- [ ] I VU-meter (se implementati) mostrano numeri che cambiano nel tempo con
|
||||||
|
l'audio reale, e il polling si ferma quando la schermata non è visibile.
|
||||||
@@ -287,3 +287,61 @@ communication channel to a chip --- either the classic USBi/ICP dongle, or
|
|||||||
DigiRadio's own TCP:8086 SigmaStudio bridge (Section~\ref{sec:adau1701-sigmastudio-tcp})
|
DigiRadio's own TCP:8086 SigmaStudio bridge (Section~\ref{sec:adau1701-sigmastudio-tcp})
|
||||||
when connecting live to a running board.
|
when connecting live to a running board.
|
||||||
\end{drnote}
|
\end{drnote}
|
||||||
|
|
||||||
|
\section{Next revision: DigiRadioFinale}
|
||||||
|
\label{sec:ss-digiradiofinale}
|
||||||
|
|
||||||
|
\begin{drcaution}[Not yet integrated into firmware]
|
||||||
|
This section documents a SigmaStudio export (\texttt{DigiRadioFinale}) captured
|
||||||
|
2026-08-25 that is not yet the program the firmware replays at boot. It expands
|
||||||
|
the parameter RAM from 74 to 224 words with several new dedicated blocks,
|
||||||
|
replacing two of the EQ-overlay workarounds in
|
||||||
|
Section~\ref{sec:ss-enhancements} with real ADI algorithm modules. Driver,
|
||||||
|
safeload API, and this manual's runtime tables will be updated once the
|
||||||
|
program is finalised.
|
||||||
|
\end{drcaution}
|
||||||
|
|
||||||
|
\begin{figure}[htbp]
|
||||||
|
\centering
|
||||||
|
\includegraphics[width=\linewidth]{sigmastudiofinale.png}
|
||||||
|
\caption{DigiRadioFinale signal chain: per-channel DC blockers and RMS
|
||||||
|
compressor, four input-side level meters, the 6-leg Param EQ, the
|
||||||
|
SuperPhat spatializer, an unconfigured general filter, master volume,
|
||||||
|
the Dynamic Bass Boost algorithm, two output-side level meters, and the
|
||||||
|
L/R limiters.}
|
||||||
|
\label{fig:ss-digiradiofinale}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{table}[htbp]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\begin{tabular}{@{}L{2.6cm}L{2.2cm}L{7.6cm}@{}}
|
||||||
|
\drhead Addresses & Block & Role \\
|
||||||
|
\midrule
|
||||||
|
0--2 & Beep1 & test tone (unchanged) \\
|
||||||
|
3 & DC1 & input trim \\
|
||||||
|
4--7 & DCB1--4 & DC blocker, one per channel \\
|
||||||
|
8 & S Splitter1 & control fan-out (unchanged) \\
|
||||||
|
9--46 & Compressor1 & RMS compander, 34-point curve \\
|
||||||
|
47--58 & 1×RTA3,4,1,2 & 4 input-side level meters (readback) \\
|
||||||
|
59--88 & Param EQ1 & 6 stereo legs (ST0--ST5) $\times$ 5 coefficients \\
|
||||||
|
89--140 & SPhat1 & SuperPhat spatializer (dedicated algorithm,
|
||||||
|
replaces the stereo-enhance EQ overlay) \\
|
||||||
|
141--145& Gen Filter1 & general 2nd-order filter, currently flat/bypassed
|
||||||
|
--- reserved, not yet configured (candidate slot
|
||||||
|
for Voice Clarifier) \\
|
||||||
|
146--147& Multiple1 & master volume L/R (unchanged) \\
|
||||||
|
148--187& Bass Boost1 & Dynamic Bass Boost algorithm (dedicated,
|
||||||
|
replaces the bass-enhance EQ overlay) \\
|
||||||
|
188--193& 1×RTA2\_2,1\_2 & 2 output-side level meters (readback) \\
|
||||||
|
194--223& Limiter2, Limiter1 & unchanged \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\caption{DigiRadioFinale parameter RAM map, by address.}
|
||||||
|
\label{tab:ss-digiradiofinale-map}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
No dedicated mute cell exists yet in this export, and Gen Filter1 (the
|
||||||
|
Voice Clarifier candidate) is still an identity filter (b0=1, all other
|
||||||
|
coefficients zero). Both are pending further SigmaStudio work before this
|
||||||
|
program replaces the one described earlier in this chapter.
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
Reference in New Issue
Block a user