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);