shithub: riscv

Download patch

ref: 21445d72f3509e0cca405aa182a1a481d0531ced
parent: 483ff27f9d5067fd597dae09161d07a3857293b6
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Wed Aug 23 19:34:17 EDT 2023

audio/flacenc: implement [-i fmt] for supported formats; fix tell callback; fix big endian and 3-byte samples

--- a/sys/man/1/audio
+++ b/sys/man/1/audio
@@ -58,18 +58,12 @@
 .br
 .B audio/flacenc
 [
-.B -b
-.I bitspersample
+.B -i
+.I fmt
 ] [
-.B -c
-.I channels
-] [
 .B -l
 .I compresslevel
 ] [
-.B -s
-.I sfreq
-] [
 .B -P
 .I padding
 ] [
@@ -142,9 +136,7 @@
 .BR -v
 (variable bitrate, VBR).
 .PP
-.I Flacenc
-and
-.I oggenc
+.I Oggenc
 accept raw PCM in the same byte order as
 .B /dev/audio
 (little-endian),
@@ -151,6 +143,15 @@
 while
 .I mp3enc -r
 expects big-endian.
+.I Flacenc
+by default expects raw PCM in the same format as
+.BR /dev/audio ,
+but also supports signed integer samples of bit widths 4 to 32, either
+little- or big-endian, one to eight channels and arbitrary
+samplerates, see
+.B -i
+option of
+.IR pcmconv .
 .SS Encoding options
 .TF "\fB-b \fP"
 .TP
--- a/sys/src/cmd/audio/flacenc/flacenc.c
+++ b/sys/src/cmd/audio/flacenc/flacenc.c
@@ -4,6 +4,7 @@
 #define _PLAN9_SOURCE
 #include <utf.h>
 #include <lib9.h>
+#include <unistd.h>
 #include "FLAC/stream_encoder.h"
 #include "FLAC/metadata.h"
 
@@ -10,17 +11,17 @@
 static FLAC__StreamEncoderReadStatus
 encwrite(FLAC__StreamEncoder *enc, FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data)
 {
-	return fwrite(buffer, 1, bytes, stdout) != bytes ?
-		FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR :
-		FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
+	if(write(1, buffer, bytes) != bytes)
+		return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
+	return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
 }
 
 static FLAC__StreamEncoderSeekStatus
 encseek(FLAC__StreamEncoder *enc, FLAC__uint64 absolute_byte_offset, void *client_data)
 {
-	return fseeko(stdout, absolute_byte_offset, SEEK_SET) != 0 ?
-		FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED :
-		FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
+	if(lseek(1, absolute_byte_offset, SEEK_SET) != absolute_byte_offset)
+		return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED;
+	return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
 }
 
 static FLAC__StreamEncoderTellStatus
@@ -27,18 +28,33 @@
 enctell(FLAC__StreamEncoder *enc, FLAC__uint64 *absolute_byte_offset, void *client_data)
 {
 	off_t off;
-
-	if((off = ftello(stdout)) < 0)
+	if((off = lseek(1, 0, 1)) < 0)
 		return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED;
-
 	*absolute_byte_offset = off;
 	return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
 }
 
+static char *
+encerr(FLAC__StreamEncoder *enc)
+{
+	switch(FLAC__stream_encoder_get_state(enc)){
+	case FLAC__STREAM_ENCODER_OK: return "ok";
+	case FLAC__STREAM_ENCODER_UNINITIALIZED: return "uninitialized";
+	case FLAC__STREAM_ENCODER_OGG_ERROR: return "ogg error";
+	case FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR: return "verify error";
+	case FLAC__STREAM_ENCODER_VERIFY_MISMATCH_IN_AUDIO_DATA: return "verify mismatch";
+	case FLAC__STREAM_ENCODER_CLIENT_ERROR: return "client error";
+	case FLAC__STREAM_ENCODER_IO_ERROR: return "io error";
+	case FLAC__STREAM_ENCODER_FRAMING_ERROR: return "framing error";
+	case FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR: return "memory alloc error";
+	}
+	return "unknown";
+}
+
 static void
 usage(void)
 {
-	fprintf(stderr, "usage: %s [-b bitspersample] [-c channels] [-l compresslevel] [-s sfreq] [-P padding] [-T field=value]\n", argv0);
+	fprintf(stderr, "usage: %s [-i fmt] [-l compresslevel] [-P padding] [-T field=value]\n", argv0);
 	exit(1);
 }
 
@@ -45,42 +61,55 @@
 int
 main(int argc, char *argv[])
 {
-	int i, n, nm, r, be, bits, chan, level, sfreq;
+	int i, n, j, r, be, bits, chan, level, sfreq, framesz;
 	FLAC__StreamMetadata_VorbisComment_Entry vc;
 	FLAC__StreamMetadata *m[2];
 	uint32_t beef = 0xdeadbeef;
 	FLAC__StreamEncoder *enc;
-	FLAC__int32 *buf;
-	int16_t *x;
+	FLAC__int32 x, *o, *obuf;
+	char *p, *fmt;
+	u8int *ibuf;
+	Rune c;
 
-	be = *((uint8_t*)&beef) == 0xde;
 	m[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
-	nm = 1;
+	i = 1;
 
+	be = 0;
 	bits = 16;
 	chan = 2;
 	sfreq = 44100;
 	level = -1;
 	ARGBEGIN{
-	case 'b':
-		bits = atoi(EARGF(usage()));
-		if(bits <= 8 || bits > 32){
-			fprintf(stderr, "bits per sample = %d not supported\n", bits);
-			exit(1);
+	case 'i':
+		fmt = EARGF(usage());
+		for(p = fmt; *p != 0;){
+			p += chartorune(&c, p);
+			n = strtol(p, &p, 10);
+			if(c == 'r')
+				sfreq = n;
+			else if(c == 'c')
+				chan = n;
+			else if(c == 's'){
+				bits = n;
+				be = 0;
+			}else if(c == 'S'){
+				bits = n;
+				be = 1;
+			}else{
+Bad:
+				fprintf(stderr, "bad format: %s", fmt);
+				exit(1);
+			}
 		}
+		if(chan < 1 || chan > 8 || bits < 4 || bits > 32 || sfreq < 1 || sfreq > 655350)
+			goto Bad;
 		break;
-	case 'c':
-		chan = atoi(EARGF(usage()));
-		break;
 	case 'l':
 		level = atoi(EARGF(usage()));
 		break;
-	case 's':
-		sfreq = atoi(EARGF(usage()));
-		break;
 	case 'P':
-		m[nm] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
-		m[nm++]->length = atoi(EARGF(usage()));
+		m[i] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+		m[i++]->length = atoi(EARGF(usage()));
 		break;
 	case 'T':
 		vc.entry = (FLAC__byte*)EARGF(usage());
@@ -94,12 +123,12 @@
 	if(argc != 0)
 		usage();
 
-	n = chan * 4096;
-	if((buf = malloc(n*4)) == NULL){
+	framesz = (bits+7)/8*chan;
+	n = IOUNIT / framesz;
+	if((ibuf = malloc(n*framesz)) == NULL || (obuf = malloc(n*4*chan)) == NULL){
 		fprintf(stderr, "no memory\n");
 		exit(1);
 	}
-	x = (int16_t*)buf + (bits > 16 ? 0 : n);
 
 	if((enc = FLAC__stream_encoder_new()) == NULL){
 		fprintf(stderr, "failed to create encoder\n");
@@ -110,7 +139,7 @@
 	FLAC__stream_encoder_set_sample_rate(enc, sfreq);
 	if(level >= 0)
 		FLAC__stream_encoder_set_compression_level(enc, level);
-	if(!FLAC__stream_encoder_set_metadata(enc, m, nm)){
+	if(!FLAC__stream_encoder_set_metadata(enc, m, i)){
 		fprintf(stderr, "failed to set metadata\n");
 		exit(1);
 	}
@@ -120,26 +149,42 @@
 		exit(1);
 	}
 
+	j = framesz/chan;
 	for(;;){
-		r = fread(x, bits > 16 ? 4 : 2, n, stdin);
+		r = fread(ibuf, framesz, n, stdin);
 		if(r < 1)
 			break;
 
-		if(bits <= 16){
-			for(i = 0; i < r; i++)
-				buf[i] = be ? x[i]<<8 | x[i]>>8 : x[i];
-		}else if(be){
-			for(i = 0; i < r; i++)
-				buf[i] = buf[i]<<24 | (buf[i]<<8)&0xff0000 | (buf[i]>>8)&0xff00 | buf[i]>>24;
+		o = obuf;
+		for(i = 0; i < r*framesz;){
+			x = 0;
+			if(be){
+				switch(j){
+				case 4: x = ibuf[i++];
+				case 3: x = x<<8 | ibuf[i++];
+				case 2: x = x<<8 | ibuf[i++];
+				case 1: x = x<<8 | ibuf[i++];
+				}
+			}else{
+				i += j;
+				switch(j){
+				case 4: x = ibuf[--i];
+				case 3: x = x<<8 | ibuf[--i];
+				case 2: x = x<<8 | ibuf[--i];
+				case 1: x = x<<8 | ibuf[--i];
+				}
+				i += j;
+			}
+			*o++ = (x << (32-bits)) >> (32-bits);
 		}
 
-		if(!FLAC__stream_encoder_process_interleaved(enc, buf, r/chan)){
-			fprintf(stderr, "encoding failed\n");
+		if(!FLAC__stream_encoder_process_interleaved(enc, obuf, r)){
+			fprintf(stderr, "encoding failed: %s\n", encerr(enc));
 			exit(1);
 		}
 	}
 	if(!FLAC__stream_encoder_finish(enc)){
-		fprintf(stderr, "encoding failed\n");
+		fprintf(stderr, "encoding failed: %s\n", encerr(enc));
 		exit(1);
 	}
 	FLAC__stream_encoder_delete(enc);