code: plan9front

Download patch

ref: 7e0179bad8fc63294696a465e1583be5c08b9417
parent: c7ec663a0bf490169a910a3bba6ff5ba094a4b4e
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sat Oct 15 15:01:46 EDT 2022

zuke: basic replay gain support

--- a/sys/man/1/zuke
+++ b/sys/man/1/zuke
@@ -130,6 +130,9 @@
 .B r
 Toggle ``repeat one''.
 .TP
+.B g
+Switch between no gain, track gain, and album gain applied.
+.TP
 .B s
 Toggle ``shuffle''.
 .TP
--- a/sys/src/cmd/audio/zuke/mkplist.c
+++ b/sys/src/cmd/audio/zuke/mkplist.c
@@ -122,6 +122,16 @@
 			aux->imagereader = f != nil;
 		}
 		break;
+	case Ttrackgain:
+		aux->rgtrack = atof(v);
+		if(strncmp(k, "R128_", 5) == 0)
+			aux->rgtrack /= 256.0;
+		break;
+	case Talbumgain:
+		aux->rgalbum = atof(v);
+		if(strncmp(k, "R128_", 5) == 0)
+			aux->rgalbum /= 256.0;
+		break;
 	}
 }
 
@@ -393,8 +403,8 @@
 			if(icyfill(m) != 0){
 				fprint(2, "%s: %r\n", argv[i]);
 				free(m);
-			}
-			sendp(cmeta, m);
+			}else
+				sendp(cmeta, m);
 		}else{
 			if(argv[i][0] == '/')
 				dir = strdup(argv[i]);
--- a/sys/src/cmd/audio/zuke/plist.c
+++ b/sys/src/cmd/audio/zuke/plist.c
@@ -21,6 +21,10 @@
 		Bprint(b, "%c %s\n", Ptrack, m->track);
 	if(m->duration > 0)
 		Bprint(b, "%c %llud\n", Pduration, m->duration);
+	if(m->rgtrack != 0.0)
+		Bprint(b, "%c %g\n", Prgtrack, m->rgtrack);
+	if(m->rgalbum != 0.0)
+		Bprint(b, "%c %g\n", Prgalbum, m->rgalbum);
 	if(m->imagesize > 0)
 		Bprint(b, "%c %d %d %d %s\n", Pimage, m->imageoffset, m->imagesize, m->imagereader, m->imagefmt);
 	Bprint(b, "\n");
--- a/sys/src/cmd/audio/zuke/plist.h
+++ b/sys/src/cmd/audio/zuke/plist.h
@@ -16,11 +16,13 @@
 	Pbasename=		'b',
 	Pdate=			'd',
 	Pduration=		'D',
+	Pfilefmt=		'f',
 	Pimage=			'i',
 	Ptitle=			't',
 	Ptrack=			'T',
 	Ppath=			'p',
-	Pfilefmt=		'f',
+	Prgtrack=		'r',
+	Prgalbum=		'R',
 
 	/* unused */
 	Pchannels=		'c',
@@ -42,6 +44,8 @@
 	char *basename;
 	char *imagefmt;
 	char *filefmt;
+	double rgtrack;
+	double rgalbum;
 	uvlong duration;
 	int numartist;
 	int imageoffset;
--- a/sys/src/cmd/audio/zuke/zuke.c
+++ b/sys/src/cmd/audio/zuke/zuke.c
@@ -24,6 +24,11 @@
 	Ctoggle,
 	Cseekrel,
 
+	Rgdisabled = 0,
+	Rgtrack,
+	Rgalbum,
+	Numrg,
+
 	Everror = 1,
 	Evready,
 
@@ -57,6 +62,7 @@
 	Channel *ev;
 	Channel *img;
 	double seek;
+	double gain;
 	int pcur;
 };
 
@@ -72,7 +78,7 @@
 
 static int debug;
 static int audio = -1;
-static int volume;
+static int volume, rg;
 static int pnotifies;
 static Playlist *pl;
 static Player *playernext;
@@ -276,10 +282,11 @@
 	Image *col;
 
 	/* seekbar playback/duration text */
-	i = snprint(tmp, sizeof(tmp), "%s%s%s",
+	i = snprint(tmp, sizeof(tmp), "%s%s%s%s",
+		rg ? (rg == Rgalbum ? "ᴬ" : "ᵀ") : "",
 		repeatone ? "¹" : "",
 		shuffle != nil ? "∫" : "",
-		(repeatone || shuffle != nil) ? " " : ""
+		(rg || repeatone || shuffle != nil) ? " " : ""
 	);
 	msec = 0;
 	if(pcurplaying >= 0){
@@ -605,6 +612,20 @@
 
 static void playerthread(void *player_);
 
+static void
+setgain(Player *player)
+{
+	if(player == nil)
+		return;
+	if(rg == Rgdisabled)
+		player->gain = 0.0;
+	if(rg == Rgtrack)
+		player->gain = getmeta(player->pcur)->rgtrack;
+	else if(rg == Rgalbum)
+		player->gain = getmeta(player->pcur)->rgalbum;
+	player->gain = pow(10.0, player->gain/20.0);
+}
+
 static Player *
 newplayer(int pcur, int loadnext)
 {
@@ -624,6 +645,7 @@
 	player->ctl = chancreate(sizeof(ulong), 0);
 	player->ev = chancreate(sizeof(ulong), 0);
 	player->pcur = pcur;
+	setgain(player);
 
 	threadcreate(playerthread, player, 32768);
 	if(getmeta(pcur)->filefmt[0] && playerret(player) < 0)
@@ -637,6 +659,18 @@
 }
 
 static void
+gain(double g, char *buf, long n)
+{
+	s16int *f;
+
+	if(g != 1.0)
+		for(f = (s16int*)buf; n >= 4; n -= 4){
+			*f = g * *f++;
+			*f = g * *f++;
+		}
+}
+
+static void
 playerthread(void *player_)
 {
 	char *buf, cmd[64], seekpos[12], *fmt;
@@ -718,6 +752,7 @@
 		if(n < 1)
 			goto next;
 		audioon();
+		gain(player->gain, buf, n);
 		boffset = iowrite(io, audio, buf, n);
 		noinit = 1;
 	}
@@ -767,6 +802,7 @@
 		boffset += n;
 		byteswritten = boffset;
 		audioon();
+		gain(player->gain, buf, n);
 		iowrite(io, audio, buf, n);
 		if(trycoverload){
 			trycoverload = 0;
@@ -930,6 +966,8 @@
 		case Pdate:    m->date = s; break;
 		case Ptitle:   m->title = s; break;
 		case Ptrack:   m->track = s; break;
+		case Prgtrack: m->rgtrack = atof(s); break;
+		case Prgalbum: m->rgalbum = atof(s); break;
 		case Ppath:
 			m->path = s;
 			m->basename = (b = utfrrune(s, '/')) == nil ? s : b+1;
@@ -1502,6 +1540,12 @@
 				freeimage(cover);
 				cover = nil;
 				full = 1;
+				break;
+			case 'g':
+				rg = (rg+1) % Numrg;
+				setgain(playercurr);
+				setgain(playernext);
+				redraw(0);
 				break;
 			case 's':
 				toggleshuffle();