ref: 03f9392b3a4583f36b8c5d961733bae00bc9417e
parent: b959ca328d306fb9af676b64720fea8a847da570
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Sep 27 19:30:08 EDT 2022
audio/mkplist: leverage parallelism ; time audio/mkplist /n/other/usr/ftrvx/m/ > /dev/null found 1317 tagged tracks 0.08u 0.31s 36.72r audio/mkplist /n/other/usr/ftrvx/m/ ; time ./7.mkplist /n/other/usr/ftrvx/m/ > /dev/null found 1317 tagged tracks 0.05u 0.02s 13.85r ./7.mkplist /n/other/usr/ftrvx/m/
--- a/sys/src/cmd/audio/zuke/mkplist.c
+++ b/sys/src/cmd/audio/zuke/mkplist.c
@@ -2,9 +2,12 @@
#include <libc.h>
#include <bio.h>
#include <tags.h>
+#include <thread.h>
#include "plist.h"
#include "icy.h"
+typedef struct Aux Aux;
+
enum
{
Maxname = 256+2, /* seems enough? */
@@ -13,13 +16,22 @@
#define MAX(a, b) (a > b ? a : b)
-static Biobuf *bf, out;
-static Meta *curr;
-static Meta *all;
-static int numall;
-static int firstiscomposer;
-static int keepfirstartist;
+struct Aux {
+ Meta;
+
+ Biobuf *f;
+ int firstiscomposer;
+ int keepfirstartist;
+};
+
+int mainstacksize = 32768;
+
static int simplesort;
+static int moddec;
+static Channel *cmeta;
+static Channel *cpath;
+static Meta **tracks;
+static int ntracks;
static char *fmts[] =
{
@@ -35,25 +47,24 @@
[Fmod] = "mod",
};
-static Meta *
-newmeta(void)
+static void
+metathread(void *)
{
- if(numall == 0){
- free(all);
- all = nil;
- }
- if(all == nil)
- all = mallocz(sizeof(Meta), 1);
- else if((numall & (numall-1)) == 0)
- all = realloc(all, numall*2*sizeof(Meta));
+ int max;
+ Meta *m;
- if(all == nil){
- sysfatal("newmeta: no memory");
- return nil;
+ max = 0;
+ for(;;){
+ if((m = recvp(cmeta)) == nil)
+ break;
+ if(ntracks+1 > max){
+ max = max ? max*2 : 1024;
+ tracks = realloc(tracks, sizeof(Meta*)*max);
+ }
+ tracks[ntracks++] = m;
}
- memset(&all[numall++], 0, sizeof(Meta));
- return &all[numall-1];
+ threadexits(nil);
}
static void
@@ -60,54 +71,55 @@
cb(Tagctx *ctx, int t, const char *k, const char *v, int offset, int size, Tagread f)
{
int i, iscomposer;
+ Aux *aux;
- USED(ctx);
+ aux = ctx->aux;
switch(t){
case Tartist:
- if(curr->numartist < Maxartist){
+ if(aux->numartist < Maxartist){
iscomposer = strcmp(k, "TCM") == 0 || strcmp(k, "TCOM") == 0;
/* prefer lead performer/soloist, helps when TP2/TPE2 is the first one and is set to "VA" */
/* always put composer first, if available */
- if(iscomposer || (!keepfirstartist && (strcmp(k, "TP1") == 0 || strcmp(k, "TPE1") == 0))){
- if(curr->numartist > 0)
- curr->artist[curr->numartist] = curr->artist[curr->numartist-1];
- curr->artist[0] = strdup(v);
- curr->numartist++;
- keepfirstartist = 1;
- firstiscomposer = iscomposer;
+ if(iscomposer || (!aux->keepfirstartist && (strcmp(k, "TP1") == 0 || strcmp(k, "TPE1") == 0))){
+ if(aux->numartist > 0)
+ aux->artist[aux->numartist] = aux->artist[aux->numartist-1];
+ aux->artist[0] = strdup(v);
+ aux->numartist++;
+ aux->keepfirstartist = 1;
+ aux->firstiscomposer = iscomposer;
return;
}
- for(i = 0; i < curr->numartist; i++){
- if(cistrcmp(curr->artist[i], v) == 0)
+ for(i = 0; i < aux->numartist; i++){
+ if(cistrcmp(aux->artist[i], v) == 0)
return;
}
- curr->artist[curr->numartist++] = strdup(v);
+ aux->artist[aux->numartist++] = strdup(v);
}
break;
case Talbum:
- if(curr->album == nil)
- curr->album = strdup(v);
+ if(aux->album == nil)
+ aux->album = strdup(v);
break;
case Ttitle:
- if(curr->title == nil)
- curr->title = strdup(v);
+ if(aux->title == nil)
+ aux->title = strdup(v);
break;
case Tdate:
- if(curr->date == nil)
- curr->date = strdup(v);
+ if(aux->date == nil)
+ aux->date = strdup(v);
break;
case Ttrack:
- if(curr->track == nil)
- curr->track = strdup(v);
+ if(aux->track == nil)
+ aux->track = strdup(v);
break;
case Timage:
- if(curr->imagefmt == nil){
- curr->imagefmt = strdup(v);
- curr->imageoffset = offset;
- curr->imagesize = size;
- curr->imagereader = f != nil;
+ if(aux->imagefmt == nil){
+ aux->imagefmt = strdup(v);
+ aux->imageoffset = offset;
+ aux->imagesize = size;
+ aux->imagereader = f != nil;
}
break;
}
@@ -116,37 +128,21 @@
static int
ctxread(Tagctx *ctx, void *buf, int cnt)
{
- USED(ctx);
- return Bread(bf, buf, cnt);
+ return Bread(((Aux*)ctx->aux)->f, buf, cnt);
}
static int
ctxseek(Tagctx *ctx, int offset, int whence)
{
- USED(ctx);
- return Bseek(bf, offset, whence);
+ return Bseek(((Aux*)ctx->aux)->f, offset, whence);
}
-static char buf[4096];
-static Tagctx ctx =
-{
- .read = ctxread,
- .seek = ctxseek,
- .tag = cb,
- .buf = buf,
- .bufsz = sizeof(buf),
- .aux = nil,
-};
-
static uvlong
modduration(char *path)
{
- static int moddec = -1;
int f, pid, p[2], n;
char t[1024], *s;
- if(moddec < 0)
- moddec = close(open("/bin/audio/moddec", OEXEC)) == 0;
if(!moddec)
return 0;
@@ -176,50 +172,73 @@
return 0;
}
-static void
+static Meta *
scanfile(char *path)
{
+ char buf[4096], *s;
+ Aux aux = {0};
+ Tagctx ctx = {
+ .read = ctxread,
+ .seek = ctxseek,
+ .tag = cb,
+ .buf = buf,
+ .bufsz = sizeof(buf),
+ .aux = &aux,
+ };
int res;
- char *s;
+ Meta *m;
- if((bf = Bopen(path, OREAD)) == nil){
+ if((aux.f = Bopen(path, OREAD)) == nil){
fprint(2, "%s: %r\n", path);
- return;
+ return nil;
}
- curr = newmeta();
- firstiscomposer = keepfirstartist = 0;
res = tagsget(&ctx);
- if(ctx.format != Funknown){
- if(res != 0)
- fprint(2, "%s: no tags\n", path);
- }else{
- numall--;
- Bterm(bf);
- return;
- }
+ Bterm(aux.f);
+ if(ctx.format == Funknown)
+ return nil;
+ if(ctx.format >= nelem(fmts))
+ sysfatal("mkplist needs a rebuild with updated libtags");
+ m = malloc(sizeof(*m));
+ memmove(m, &aux.Meta, sizeof(*m));
+ if(res != 0)
+ fprint(2, "%s: no tags\n", path);
if(ctx.duration == 0){
- if(ctx.format == Fit ||
- ctx.format == Fxm ||
- ctx.format == Fs3m ||
- ctx.format == Fmod)
+ if(ctx.format == Fit || ctx.format == Fxm || ctx.format == Fs3m || ctx.format == Fmod)
ctx.duration = modduration(path);
if(ctx.duration == 0)
fprint(2, "%s: no duration\n", path);
}
- if(curr->title == nil){
+ m->duration = ctx.duration;
+ if(m->title == nil){
if((s = utfrrune(path, '/')) == nil)
s = path;
- curr->title = strdup(s+1);
+ m->title = strdup(s+1);
}
- curr->path = strdup(path);
- curr->duration = ctx.duration;
- if(ctx.format >= nelem(fmts))
- sysfatal("mkplist needs a rebuild with updated libtags");
- curr->filefmt = fmts[ctx.format];
- Bterm(bf);
+ m->path = strdup(path);
+ m->filefmt = fmts[ctx.format];
+
+ return m;
}
+static void
+tagreadproc(void *cexit)
+{
+ char *path;
+ Meta *m;
+
+ for(;;){
+ if(recv(cpath, &path) != 1)
+ break;
+ if((m = scanfile(path)) != nil)
+ sendp(cmeta, m);
+ free(path);
+ }
+ sendul(cexit, 0);
+
+ threadexits(nil);
+}
+
static int
scan(char **dir, int depth)
{
@@ -241,7 +260,7 @@
for(n = 0, buf = nil; n >= 0;){
if((n = dirread(dirfd, &buf)) < 0){
path[len] = 0;
- scanfile(path);
+ sendp(cpath, strdup(path));
break;
}
if(n == 0){
@@ -259,7 +278,7 @@
sysfatal("Maxname=%d was a bad choice", Maxname);
if((d->mode & DMDIR) == 0){
- scanfile(path);
+ sendp(cpath, strdup(path));
}else if(depth < Maxdepth){ /* recurse into the directory */
scan(dir, depth+1);
path = *dir;
@@ -282,8 +301,8 @@
char *ae, *be;
int i, x;
- a = a_;
- b = b_;
+ a = *(Meta**)a_;
+ b = *(Meta**)b_;
if(simplesort)
return cistrcmp(a->path, b->path);
@@ -328,14 +347,17 @@
}
void
-main(int argc, char **argv)
+threadmain(int argc, char **argv)
{
- char *dir, wd[4096];
- int i;
+ char *dir, *s, wd[4096];
+ Channel *cexit;
+ int i, nproc;
+ Biobuf out;
+ Meta *m;
ARGBEGIN{
case 's':
- simplesort = 1;
+ simplesort++;
break;
default:
usage();
@@ -343,18 +365,36 @@
if(argc < 1)
usage();
- getwd(wd, sizeof(wd));
+ if(getwd(wd, sizeof(wd)) == nil)
+ sysfatal("%r");
+ moddec = access("/bin/audio/moddec", AEXEC) == 0;
+ cmeta = chancreate(sizeof(Meta*), 0);
+ cpath = chancreate(sizeof(char*), 32);
+ cexit = chancreate(sizeof(ulong), 0);
+ if((s = getenv("NPROC")) == nil)
+ s = strdup("1");
+ if((nproc = atoi(s)-1) < 1)
+ nproc = 1;
+ free(s);
+ for(i = 0; i < nproc; i++)
+ proccreate(tagreadproc, cexit, 16384);
+
+ threadcreate(metathread, nil, 4096);
+
Binit(&out, 1, OWRITE);
for(i = 0; i < argc; i++){
if(strncmp(argv[i], "http://", 7) == 0 || strncmp(argv[i], "https://", 8) == 0){
- curr = newmeta();
- curr->title = argv[i];
- curr->path = argv[i];
- curr->filefmt = "";
- if(icyfill(curr) != 0)
+ m = mallocz(sizeof(*m), 1);
+ m->title = argv[i];
+ m->path = argv[i];
+ m->filefmt = "";
+ if(icyfill(m) != 0){
fprint(2, "%s: %r\n", argv[i]);
+ free(m);
+ }
+ sendp(cmeta, m);
}else{
if(argv[i][0] == '/')
dir = strdup(argv[i]);
@@ -362,17 +402,26 @@
dir = smprint("%s/%s", wd, argv[i]);
cleanname(dir);
scan(&dir, 0);
+ free(dir);
}
}
- qsort(all, numall, sizeof(Meta), cmpmeta);
- for(i = 0; i < numall; i++){
- if(all[i].numartist < 1)
- fprint(2, "no artists: %s\n", all[i].path);
- if(all[i].title == nil)
- fprint(2, "no title: %s\n", all[i].path);
- printmeta(&out, all+i);
+
+ chanclose(cpath);
+ for(i = 0; i < nproc; i++)
+ recvul(cexit);
+ chanclose(cmeta);
+
+ qsort(tracks, ntracks, sizeof(Meta*), cmpmeta);
+ for(i = 0; i < ntracks; i++){
+ if(tracks[i]->numartist < 1)
+ fprint(2, "no artists: %s\n", tracks[i]->path);
+ if(tracks[i]->title == nil)
+ fprint(2, "no title: %s\n", tracks[i]->path);
+ printmeta(&out, tracks[i]);
}
+
Bterm(&out);
- fprint(2, "found %d tagged tracks\n", numall);
- exits(nil);
+ fprint(2, "found %d tagged tracks\n", ntracks);
+
+ threadexitsall(nil);
}