merge
This commit is contained in:
commit
ebaddcf030
7 changed files with 195 additions and 18 deletions
177
sys/man/1/zuke
Normal file
177
sys/man/1/zuke
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
.TH ZUKE 1
|
||||||
|
.SH NAME
|
||||||
|
mkplist, zuke \- graphical music player
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B audio/mkplist
|
||||||
|
.I directory/file/URL [...]
|
||||||
|
.br
|
||||||
|
.B audio/zuke
|
||||||
|
[
|
||||||
|
.B -s
|
||||||
|
] [
|
||||||
|
.B -c
|
||||||
|
.I columns
|
||||||
|
]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.PP
|
||||||
|
.I Zuke
|
||||||
|
is a graphical music player that reads a playlist from standard input
|
||||||
|
and presents an interface to play music. Playlists are generated
|
||||||
|
by
|
||||||
|
.IR mkplist ,
|
||||||
|
which accepts files, directories, and URLs as its arguments,
|
||||||
|
and writes the resulting playlist to standard output.
|
||||||
|
.PP
|
||||||
|
Formats supported by
|
||||||
|
.I zuke
|
||||||
|
are: MP3, OGG/Vorbis, FLAC, and WAV. With additional programs, Opus,
|
||||||
|
modules and M4A (AAC) can be played.
|
||||||
|
.I Zuke
|
||||||
|
also supports network streams, such as IceCast.
|
||||||
|
Option
|
||||||
|
.B -s
|
||||||
|
enables ``shuffle'' mode on start.
|
||||||
|
.PP
|
||||||
|
The columns (and their order) displayed can be changed by passing the
|
||||||
|
following letters along
|
||||||
|
.B -c
|
||||||
|
option:
|
||||||
|
.RS
|
||||||
|
.TF "c"
|
||||||
|
.TP
|
||||||
|
.B A
|
||||||
|
Artist
|
||||||
|
.TP
|
||||||
|
.B a
|
||||||
|
Album
|
||||||
|
.TP
|
||||||
|
.B b
|
||||||
|
File basename
|
||||||
|
.TP
|
||||||
|
.B t
|
||||||
|
Title
|
||||||
|
.TP
|
||||||
|
.B D
|
||||||
|
Duration
|
||||||
|
.TP
|
||||||
|
.B d
|
||||||
|
Date
|
||||||
|
.TP
|
||||||
|
.B T
|
||||||
|
Track number
|
||||||
|
.TP
|
||||||
|
.B p
|
||||||
|
Full file path
|
||||||
|
.PD
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
By default, artist, album, title, and duration are displayed, which
|
||||||
|
corresponds to
|
||||||
|
.B "-c AatD."
|
||||||
|
.PP
|
||||||
|
.I Zuke
|
||||||
|
can be controlled with a mouse, keyboard, and plumber. Button 1
|
||||||
|
selects a track, button 2 plays a track. Clicking on the seek bar
|
||||||
|
changes the playback position accordingly. On the right of the seek
|
||||||
|
bar, current position, track duration and volume are displayed. ``∫''
|
||||||
|
is shown if shuffle mode is enabled.
|
||||||
|
.PP
|
||||||
|
.I Zuke
|
||||||
|
provides a number of keyboard controls:
|
||||||
|
.RS
|
||||||
|
.TF "p c Space"
|
||||||
|
.TP
|
||||||
|
.B -
|
||||||
|
Lower volume.
|
||||||
|
.TP
|
||||||
|
.B + =
|
||||||
|
Increase volume.
|
||||||
|
.TP
|
||||||
|
.B ← →
|
||||||
|
Seek 10 seconds back/forward.
|
||||||
|
.TP
|
||||||
|
.B , .
|
||||||
|
Seek 60 seconds back/forward.
|
||||||
|
.TP
|
||||||
|
.B ↑ ↓ Pgup Pgdown Home End
|
||||||
|
Move within the playlist.
|
||||||
|
.TP
|
||||||
|
.B o i
|
||||||
|
Jump to current track.
|
||||||
|
.TP
|
||||||
|
.B Enter
|
||||||
|
Play selected track.
|
||||||
|
.TP
|
||||||
|
.B > b
|
||||||
|
Skip to next track.
|
||||||
|
.TP
|
||||||
|
.B < z
|
||||||
|
Skip to previous track.
|
||||||
|
.TP
|
||||||
|
.B v
|
||||||
|
Stop
|
||||||
|
.TP
|
||||||
|
.B p c Space
|
||||||
|
Pause/Resume.
|
||||||
|
.TP
|
||||||
|
.B s
|
||||||
|
Toggle ``shuffle''.
|
||||||
|
.TP
|
||||||
|
.B q Del
|
||||||
|
Quit.
|
||||||
|
.TP
|
||||||
|
.B /
|
||||||
|
Search forward.
|
||||||
|
.TP
|
||||||
|
.B ?
|
||||||
|
Search backwards.
|
||||||
|
.TP
|
||||||
|
.B n
|
||||||
|
Repeat search forward.
|
||||||
|
.TP
|
||||||
|
.B N
|
||||||
|
Repeat search backwards.
|
||||||
|
.RE
|
||||||
|
.PD
|
||||||
|
.PP
|
||||||
|
.I Zuke
|
||||||
|
can be controlled by emulating key presses via the plumber port
|
||||||
|
.IR audio .
|
||||||
|
.PP
|
||||||
|
Files present in the current playlist can be plumbed too, and will be
|
||||||
|
played immediately.
|
||||||
|
.SH EXAMPLES
|
||||||
|
Generate a playlist:
|
||||||
|
.IP
|
||||||
|
.EX
|
||||||
|
audio/mkplist /usr/glenda/music \\
|
||||||
|
file.mp3 \\
|
||||||
|
http://stream.nauticradio.net:14280/ > music.plist
|
||||||
|
.EE
|
||||||
|
.PP
|
||||||
|
Playing a playlist:
|
||||||
|
.IP
|
||||||
|
.EX
|
||||||
|
audio/zuke < music.plist
|
||||||
|
.EE
|
||||||
|
.PP
|
||||||
|
Append to a playlist:
|
||||||
|
.IP
|
||||||
|
.EX
|
||||||
|
audio/mkplist /n/moremusic >> music.plist
|
||||||
|
.EE
|
||||||
|
.PP
|
||||||
|
Skip to the next track using plumber:
|
||||||
|
.IP
|
||||||
|
.EX
|
||||||
|
plumb -d audio 'key >'
|
||||||
|
.EE
|
||||||
|
.SH SEE ALSO
|
||||||
|
.IR play (1)
|
||||||
|
.SH SOURCE
|
||||||
|
.B /sys/src/cmd/audio/zuke
|
||||||
|
.br
|
||||||
|
.B /sys/src/cmd/audio/libtags
|
||||||
|
.SH HISTORY
|
||||||
|
.I Zuke
|
||||||
|
first appeared in 9front (April, 2021).
|
|
@ -3,12 +3,12 @@
|
||||||
int
|
int
|
||||||
tagit(Tagctx *ctx)
|
tagit(Tagctx *ctx)
|
||||||
{
|
{
|
||||||
char d[4+26+1], o[26*UTFmax+1];
|
uchar d[4+26+1], o[26*2+1];
|
||||||
|
|
||||||
if(ctx->read(ctx, d, 4+26) != 4+26 || memcmp(d, "IMPM", 4) != 0)
|
if(ctx->read(ctx, d, 4+26) != 4+26 || memcmp(d, "IMPM", 4) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
d[4+26] = 0;
|
d[4+26] = 0;
|
||||||
if(cp437toutf8(o, sizeof(o), d+4, 26) > 0)
|
if(iso88591toutf8(o, sizeof(o), d+4, 26) > 0)
|
||||||
txtcb(ctx, Ttitle, "", o);
|
txtcb(ctx, Ttitle, "", o);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -16,6 +16,7 @@ static char *variants[] =
|
||||||
"CD81",
|
"CD81",
|
||||||
"OCTA",
|
"OCTA",
|
||||||
"OKTA",
|
"OKTA",
|
||||||
|
"10CH",
|
||||||
"16CN",
|
"16CN",
|
||||||
"32CN",
|
"32CN",
|
||||||
nil,
|
nil,
|
||||||
|
@ -24,7 +25,7 @@ static char *variants[] =
|
||||||
int
|
int
|
||||||
tagmod(Tagctx *ctx)
|
tagmod(Tagctx *ctx)
|
||||||
{
|
{
|
||||||
char d[20], o[20*UTFmax+1];
|
uchar d[20], o[20*2+1];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if(ctx->seek(ctx, 1080, 0) != 1080)
|
if(ctx->seek(ctx, 1080, 0) != 1080)
|
||||||
|
@ -41,7 +42,7 @@ tagmod(Tagctx *ctx)
|
||||||
return -1;
|
return -1;
|
||||||
if(ctx->read(ctx, d, 20) != 20)
|
if(ctx->read(ctx, d, 20) != 20)
|
||||||
return -1;
|
return -1;
|
||||||
if(cp437toutf8(o, sizeof(o), d, 20) > 0)
|
if(iso88591toutf8(o, sizeof(o), d, 20) > 0)
|
||||||
txtcb(ctx, Ttitle, "", o);
|
txtcb(ctx, Ttitle, "", o);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -41,19 +41,20 @@ tagscallcb(Tagctx *ctx, int type, const char *k, char *s, int offset, int size,
|
||||||
char *e;
|
char *e;
|
||||||
|
|
||||||
if(f == nil && size == 0){
|
if(f == nil && size == 0){
|
||||||
while(*s <= ' ' && *s)
|
while((uchar)*s <= ' ' && *s)
|
||||||
s++;
|
s++;
|
||||||
e = s + strlen(s);
|
e = s + strlen(s);
|
||||||
while(e != s && e[-1] <= ' ')
|
while(e != s && (uchar)e[-1] <= ' ')
|
||||||
e--;
|
e--;
|
||||||
*e = 0;
|
*e = 0;
|
||||||
}
|
}
|
||||||
|
if(*s){
|
||||||
|
ctx->tag(ctx, type, k, s, offset, size, f);
|
||||||
if(type != Tunknown){
|
if(type != Tunknown){
|
||||||
ctx->found |= 1<<type;
|
ctx->found |= 1<<type;
|
||||||
ctx->num++;
|
ctx->num++;
|
||||||
}
|
}
|
||||||
if(*s)
|
}
|
||||||
ctx->tag(ctx, type, k, s, offset, size, f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -41,4 +41,4 @@ void cbvorbiscomment(Tagctx *ctx, char *k, char *v);
|
||||||
|
|
||||||
void tagscallcb(Tagctx *ctx, int type, const char *k, char *s, int offset, int size, Tagread f);
|
void tagscallcb(Tagctx *ctx, int type, const char *k, char *s, int offset, int size, Tagread f);
|
||||||
|
|
||||||
#define txtcb(ctx, type, k, s) tagscallcb(ctx, type, k, (const char*)s, 0, 0, nil)
|
#define txtcb(ctx, type, k, s) tagscallcb(ctx, type, k, (char*)s, 0, 0, nil)
|
||||||
|
|
|
@ -3,12 +3,11 @@
|
||||||
int
|
int
|
||||||
tagxm(Tagctx *ctx)
|
tagxm(Tagctx *ctx)
|
||||||
{
|
{
|
||||||
char d[17+20+1], o[20*UTFmax+1], *s;
|
char d[17+20+1], o[20*UTFmax+1];
|
||||||
|
|
||||||
if(ctx->read(ctx, d, 17+20) != 17+20 || memcmp(d, "Extended Module: ", 17) != 0)
|
if(ctx->read(ctx, d, 17+20) != 17+20 || cistrncmp(d, "Extended Module: ", 17) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
d[17+20] = 0;
|
d[17+20] = 0;
|
||||||
for(s = d+17; *s == ' '; s++);
|
|
||||||
if(cp437toutf8(o, sizeof(o), d+17, 20) > 0)
|
if(cp437toutf8(o, sizeof(o), d+17, 20) > 0)
|
||||||
txtcb(ctx, Ttitle, "", o);
|
txtcb(ctx, Ttitle, "", o);
|
||||||
|
|
||||||
|
|
|
@ -175,10 +175,9 @@ addchild(Mesg *p, Mesg *m, int d)
|
||||||
|
|
||||||
assert(m->parent == nil);
|
assert(m->parent == nil);
|
||||||
for(q = p; q != nil; q = q->parent){
|
for(q = p; q != nil; q = q->parent){
|
||||||
if(ideq(m->messageid, q->messageid)){
|
/* some messages refer to themselves */
|
||||||
fprint(2, "wonky message replies to self\n");
|
if(ideq(m->messageid, q->messageid))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
if(m->time > q->time)
|
if(m->time > q->time)
|
||||||
q->time = m->time;
|
q->time = m->time;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue