Airplay et réglage du volume sur Bose SA-3

Airplay et Bose SA-3

Il s’agit du même montage à la base que pour l’article Airplay sur un ampli des annees 1990.

Le Bose SA-3 est utilisé hors de son environnement Bose (LifeStyle), et donc sans télécommande, ni commande via un autre équipement Bose. Il n’y a pas de commande (clavier) sur cet ampli.

Mais le Bose SA-3 à quelques particularités intéressantes:

  • Entrée VCA: Volume Control Adjustment. Cela permet de connecter un contrôle de volume distant. En pratique on peut connecter un potentiomètre de 10kO pour régler le volume (lorsque le SA-3 est utilisé de manière autonome, c’est à dire sans la télécommande LifeStyle).
  • Veille automatique. Via le Dipswitch B (sur ON), l’ampli se reveille automatiquement lorsqu’un signal audio est envoyé.
  • Moyen de communication numérique sur la prise DIN. Permet d’envoyer des commandes numériques (genre I2C ou 1wire) à l’amplificateur pour le reveiller, régler le volume, ou changer d’entrée.

Réglage du volume : électronique

Il y a juste à connecter un module de volume MCP41010 (de 10kO donc), sur le Raspberry en utilisant le port SPI1 de ce dernier.

Raspberry brocheRaspberry signalMCP41010 signal
19 (GPIO 10)MOSI (via une resistance de 580 Ohm)SDI/SDO
21 (GPIO 9)MISOSDI/SDO
23 (GPIO 11)SCLKCLK
26 (GPIO 7)CE1CS
13.3VVCC
6 ou 9GNDGND

La partie potentiomètre du MCP41010 (borne du milieu et une borne du côté) est à connecter à l’entrée VCA du SA-3.

Expérimentations

Les premières expérimentations ont montré que la commande du potentiomètre n’avait pas un effet linéaire.

Le schéma du SA-2/3 à montré que le potentiomètre externe (J6) fait partie d’un pont diviseur de tension (associé à R17). C14 et R45 doivent filtrer l’entrée. BAV99 est une protection. 5_PF4 est une entrée d’un microcontrolleur (probablement une entrée ADC).

J’ai utilisé une LUT remplie a la main pour avoir quelque chose d’utilisable via Airplay (plafonne à 64 soit environ 2,5kO, et montée log forte après 40). A voir dans le code C.

Le potentiomètre ayant une plage de 10kO, pour l’agrandir, on peut mettre une résistance de en parallèle du potentiomètre pour agrandir la plage. Cette résistance peut être placée sur le bornier du potentiomètre. Après différent mesures test, la plage lue par la commande de l’amplificateur est une tension entre 0 et 2,0V approximativement (il est possible que l’ADC numérique derrière soit sur 2,5V). Il faut utiliser une résistance d’environ 1,5kO pour utiliser la plus grande plage de la résistance, sauf que comme ce n’est pas linéaire, le début de la plage évolue peu. Actuellement j’ai 10kO.

Pour une plage 0-2Vresistance maxnombre de pas maxpas à 50%
potentiomètre seul1300 Ohm3416
avec résistance 10kO en //1350 Ohm3918
avec résistance 4,7kO en //1300 Ohm4620
avec résistance 1,8kO en //1300 Ohm13026
avec résistance 1,5kO en //1300 Ohm24930
Tension commandée (V) en fonction du volume demandé (dB) avec résistance 4,7kO

Réglage du volume: logiciel

Voici le code source:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>

#include <linux/types.h>
#include <linux/spi/spidev.h>

#include <sys/ioctl.h>

#define RES_10KO

const unsigned char volume_lut[] = {
#ifdef RES_NO
      64, 53, 45, 40, 37, 33, 31, 29, //  0: 87% - 100%
      28, 27, 26, 25, 24, 23, 22, 21, //  8: 75%
      20, 19, 19, 18, 18, 17, 17, 16, // 16: 62%
      16, 15, 15, 14, 14, 13, 13, 12, // 24: 50% -
      12, 11, 11, 10, 10,  9,  9,  9, // 32: 38%
       8,  8,  8,  7,  7,  6,  6,  5, // 40: 25%
       5,  4,  4,  3,  3,  2,  2,  1, // 48: 12%
       1,  0,  0,  0              // 56:  0%
#endif
#ifdef RES_10KO
      64, 53, 45, 40, 37, 33, 32, 31, //  0: 87% - 100%
      30, 29, 28, 27, 26, 25, 24, 23, //  8: 75%
      22, 21, 20, 19, 18, 17, 16, 15, // 16: 62%
      15, 14, 14, 13, 13, 12, 12, 11, // 24: 50% -
      11, 10, 10,  9,  9,  8,  8,  7, // 32: 38%
       7,  6,  6,  5,  5,  5,  4,  4, // 40: 25%
       4,  3,  3,  3,  2,  2,  2,  1, // 48: 12%
       1,  1,  0,  0                  // 56:  0%
#endif
    };

int main(int argc, char *argv[])
{
        int fd_spi;
        float volume;
	unsigned int position;
        unsigned char byte[2];

        const unsigned int speed = 250000;
	const unsigned int pos_max = 64; /* software limitation of the position: 51 positions equiv to 2kO */
	const float airplay_min_vol = -30.0;
	const float airplay_max_vol = 0.0;

        if (argc != 3) {
                fprintf(stderr, "usage: %s <spi-port> <volume_dB>\n", argv[0]);
                exit(EXIT_FAILURE);
        }
        fd_spi = open(argv[1], O_RDWR);
        if (fd_spi < 0) {
                perror(argv[1]);
                exit(EXIT_FAILURE);
        }

        if (ioctl(fd_spi, SPI_IOC_WR_MAX_SPEED_HZ, &speed) != 0) {
                perror("ioctl");
                exit(EXIT_FAILURE);
        }

        if (sscanf(argv[2], "%f", &volume) != 1) {
		fprintf(stderr, "float value expected\n");
		perror("value");
		exit(EXIT_FAILURE);
        }

	if (volume > airplay_max_vol) volume = airplay_max_vol;
	if (volume < airplay_min_vol) volume = airplay_min_vol;
	
	// position = (pos_max - abs((int)((pos_max * volume) / (airplay_max_vol - airplay_min_vol))));
	//position = (unsigned int)(((float)pos_max * (volume - airplay_min_vol)) / (-airplay_min_vol));
	// Conversion des dB (0.0 à -30.0) en index de tableau (0 à 60)
        // Un pas de 0.5 dB correspond à multiplier par 2.
	position = volume_lut[(int)((-volume * 2.0f) + 0.5f)];
	if (position > pos_max) position = pos_max;

	printf("Volume: %f, position: %d/%d\n", volume, position, pos_max);

		byte[0] = (unsigned char) (0x11);
                byte[1] = (unsigned char)(position & 0xff);
		printf("SPI:0x%02x%02x\n",byte[0],byte[1]);
                if (write(fd_spi, &byte, 2) != 2) {
                        perror("write");
                        exit(EXIT_FAILURE);
                }

        close(fd_spi);
        return EXIT_SUCCESS;
}

Pour le compiler:

make mcp41010_volume

Déplacez et donnez les bons droits à l’executable.

Dans le fichier de configuration de shairport-sync, désactiver le réglage du volume en logiciel (interne avant la sortie analogique), et ajouter le script de commande de la résistance externe:

...
	ignore_volume_control = "yes"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to.
//	volume_range_db = 60 ; // use this advanced setting to set the range, in dB, you want between the maximum volume and the minimum volume. Range is 30 to 150 dB. Leave it commented out to use mixer's native range.
//	volume_max_db = 0.0 ; // use this advanced setting, which must have a decimal point in it, to set the maximum volume, in dB, you wish to use.
//		The setting is for the hardware mixer, if chosen, or the software mixer otherwise. The value must be in the mixer's range (0.0 to -96.2 for the software mixer).
//		Leave it commented out to use mixer's maximum volume.
//	volume_control_profile = "standard" ; // use this advanced setting to specify how the airplay volume is transferred to the mixer volume.
//		"standard" makes the volume change more quickly at lower volumes and slower at higher volumes.
//		"flat" makes the volume change at the same rate at all volumes.
//		"dasl_tapered" is similar to "standard" - it makes the volume change more quickly at lower volumes and slower at higher volumes.
//			The basic idea behind dasl_tapered is that a given percentage change in volume should result in the same percentage change in
//			perceived loudness. For instance, doubling the volume level should result in doubling the perceived loudness.
//			With the range of AirPlay volume being from -30 to 0, doubling the volume from -22.5 to -15 results in an increase of 10 dB.
//			Similarly, doubling the volume from -15 to 0 results in an increase of 10 dB.
//			For compatibility with mixers having a restricted attenuation range (e.g. 30 dB), "dasl_tapered" will switch to a flat profile at low AirPlay volumes.
//	volume_control_combined_hardware_priority = "no"; // when extending the volume range by combining the built-in software attenuator with the hardware mixer attenuator, set this to "yes" to reduce volume by using the hardware mixer first, then the built-in software attenuator.
//	default_airplay_volume = -24.0; // this is the suggested volume after a reset or after the high_volume_threshold has been exceed and the high_volume_idle_timeout_in_minutes has passed
//	run_this_when_volume_is_set = "/full/path/to/application/and/args"; //	Run the specified application whenever the volume control is set or changed.
//		The desired AirPlay volume is appended to the end of the command line – leave a space if you want it treated as an extra argument.
//		AirPlay volume goes from 0.0 to -30.0 and -144.0 means "mute".
	run_this_when_volume_is_set = "/usr/local/sbin/mcp41010_volume  /dev/spidev0.1 "
...

La borne du côté du potentiomètre sera peut être à échanger si la commande du volume est inversée (augmente quand la commande baisse par exemple).