commit 9fe644f0d99375ffd3011d8828f7dbd0fb103af0
parent d68d0f39ab7675745e7d177f6774736f7ec58783
Author: Louis Burda <quent.burda@gmail.com>
Date: Sat, 4 Dec 2021 13:48:01 +0100
Added more gui interaction
Diffstat:
M | .gitignore | | | 1 | + |
M | Makefile | | | 6 | +++--- |
M | history.c | | | 6 | +++++- |
M | link.c | | | 64 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
M | link.h | | | 15 | +++++++++++---- |
M | main.c | | | 406 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------- |
A | player.c | | | 113 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | player.h | | | 54 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tag.c | | | 53 | +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tag.h | | | 22 | ++++++++++++++++++++++ |
A | track.c | | | 37 | +++++++++++++++++++++++++++++++++++++ |
A | track.h | | | 25 | +++++++++++++++++++++++++ |
M | util.c | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
M | util.h | | | 4 | ++++ |
14 files changed, 759 insertions(+), 93 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,3 +1,4 @@
vgcore*
main
+env.sh
*.o
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
-CFLAGS = -I .
-LDLIBS = -lcurses -lreadline
+CFLAGS = -I . -g
+LDLIBS = -lcurses -lreadline -lportaudio -lsndfile
.PHONY: all main
@@ -11,5 +11,5 @@ clean:
%.o: %.c %.h
$(CC) -c -o $@ $< $(CFLAGS) $(LDLIBS)
-main: main.c util.o history.o link.o
+main: main.c util.o history.o link.o player.o tag.o track.o
$(CC) -o $@ $^ $(CFLAGS) $(LDLIBS)
diff --git a/history.c b/history.c
@@ -103,7 +103,8 @@ history_add(struct history *history, struct inputln *line)
struct link *back;
if (list_len(&history->list) == HISTORY_MAX) {
- back = list_back(&history->list);
+ /* pop last item to make space */
+ back = link_back(&history->list);
back->prev->next = NULL;
ln = UPCAST(back, struct inputln);
inputln_free(ln);
@@ -163,6 +164,7 @@ inputln_addch(struct inputln *line, wchar_t c)
for (i = line->len; i > line->cur; i--)
line->buf[i] = line->buf[i-1];
line->buf[line->cur] = c;
+
line->len++;
line->cur++;
}
@@ -177,8 +179,10 @@ inputln_del(struct inputln *line, int n)
n = MIN(n, line->cur);
for (i = line->cur; i <= line->len; i++)
line->buf[i-n] = line->buf[i];
+
for (i = line->len - n; i <= line->len; i++)
line->buf[i] = 0;
+
line->len -= n;
line->cur -= n;
}
diff --git a/link.c b/link.c
@@ -1,30 +1,50 @@
#include "link.h"
+#include "util.h"
int
-list_len(struct link *list)
+list_len(struct link *head)
{
struct link *iter;
int len;
+ ASSERT(head != NULL);
+
len = 0;
- for (iter = list; iter; iter = iter->next)
+ for (iter = head->next; iter; iter = iter->next)
len += 1;
return len;
}
+int
+list_ffind(struct link *head, struct link *link)
+{
+ struct link *iter;
+
+ ASSERT(head != NULL);
+
+ for (iter = head->next; iter && iter != link; iter = iter->next);
+
+ return (iter == link);
+}
+
struct link *
-list_back(struct link *link)
+link_back(struct link *link)
{
+ ASSERT(link != NULL);
+
for (; link->next; link = link->next);
+
return link;
}
void
-link_prepend(struct link *list, struct link *link)
+link_prepend(struct link *cur, struct link *link)
{
- link->prev = list->prev;
- link->next = list;
+ ASSERT(cur != NULL && link != NULL);
+
+ link->prev = cur->prev;
+ link->next = cur;
if (link->prev)
link->prev->next = link;
@@ -33,10 +53,12 @@ link_prepend(struct link *list, struct link *link)
}
void
-link_append(struct link *list, struct link *link)
+link_append(struct link *cur, struct link *link)
{
- link->prev = list;
- link->next = list->next;
+ ASSERT(cur != NULL && link != NULL);
+
+ link->prev = cur;
+ link->next = cur->next;
if (link->prev)
link->prev->next = link;
@@ -47,8 +69,32 @@ link_append(struct link *list, struct link *link)
void
link_pop(struct link *link)
{
+ ASSERT(link != NULL);
+
if (link->prev)
link->prev->next = link->next;
if (link->next)
link->next->prev = link->prev;
}
+
+struct link *
+link_iter(struct link *link, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (!link) return NULL;
+ link = link->next;
+ }
+
+ return link;
+}
+
+void
+link_push_back(struct link *cur, struct link *link)
+{
+ struct link *back;
+
+ back = link_back(cur);
+ link_append(back, link);
+}
diff --git a/link.h b/link.h
@@ -8,16 +8,23 @@
(type *)( (char *)__mptr - OFFSET(type, link) ); })
#define LIST_HEAD ((struct link) { .prev = NULL, .next = NULL })
+#define LINK_EMPTY ((struct link) { 0 })
struct link {
- struct link *prev, *next;
+ struct link *prev;
+ struct link *next;
};
-int list_len(struct link *list);
-struct link* list_back(struct link *list);
+/* list_XXX functions operate on the list head */
-void link_push_back(struct link *list, struct link *link);
+int list_len(struct link *head);
+int list_ffind(struct link *head, struct link *link);
+struct link *link_back(struct link *list);
void link_prepend(struct link *list, struct link *link);
void link_append(struct link *list, struct link *link);
void link_pop(struct link *link);
+
+struct link *link_iter(struct link *link, int n);
+
+void link_push_back(struct link *list, struct link *link);
diff --git a/main.c b/main.c
@@ -1,39 +1,52 @@
#define _XOPEN_SOURCE 600
+#define _DEFAULT_SOURCE
#include "util.h"
#include "link.h"
#include "history.h"
+#include "tag.h"
+#include "track.h"
+#include "player.h"
+#include "sndfile.h"
+#include "portaudio.h"
#include "curses.h"
+#include <dirent.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <wchar.h>
#include <wctype.h>
-
-#define ATTR_ON(win, attr) wattr_on((win), (attr), NULL)
-#define ATTR_OFF(win, attr) wattr_off((win), (attr), NULL)
-#define STYLE_ON(win, style) ATTR_ON((win), COLOR_PAIR(STYLE_ ## style))
-#define STYLE_OFF(win, style) ATTR_OFF((win), COLOR_PAIR(STYLE_ ## style))
-
+#undef KEY_ENTER
+#define KEY_ENTER '\n'
+#define KEY_SPACE ' '
#define KEY_ESC '\x1b'
#define KEY_TAB '\t'
#define KEY_CTRL(c) ((c) & ~0x60)
+#define ATTR_ON(win, attr) wattr_on(win, attr, NULL)
+#define ATTR_OFF(win, attr) wattr_off(win, attr, NULL)
enum {
STYLE_DEFAULT,
STYLE_TITLE,
- STYLE_PANE_SEP
+ STYLE_PANE_SEP,
+ STYLE_ITEM_SEL,
+ STYLE_ITEM_HOVER,
+ STYLE_ITEM_HOVER_SEL,
+ STYLE_COUNT
};
enum {
- EXECUTE,
- SEARCH
+ IMODE_EXECUTE,
+ IMODE_SEARCH
};
struct pane;
@@ -53,33 +66,15 @@ struct pane {
pane_updater update;
};
-struct track {
- char *title;
- char *artist;
- float duration;
- struct link *tags;
-
- struct link link;
-};
-
-struct track_ref {
- struct track *track;
-
- struct link link;
-};
-
-struct tag {
- const char *name;
-
- struct link link;
-};
-
struct cmd {
const char *name;
cmd_handler func;
};
+int style_attrs[STYLE_COUNT] = { 0 };
+
+const char *datadir;
int scrw, scrh;
int quit;
@@ -92,8 +87,10 @@ struct pane *const panes[] = {
&pane_bot
};
+struct link tags;
+
struct track *track_sel;
-struct track *tracks;
+struct link tracks;
struct link playlist;
int track_paused;
@@ -102,10 +99,21 @@ struct history search_history, command_history;
struct history *history;
int cmd_show, cmd_mode;
+int tag_index;
+struct link tags_sel;
+
+int track_index;
+
void init(void);
void cleanup(void);
+void data_load(void);
+void tracks_load(struct tag *tag);
+
+void data_save(void);
+void tracks_save(struct tag *tag);
+
void resize(void);
void pane_resize(struct pane *pane,
int sx, int sy, int ex, int ey);
@@ -113,21 +121,32 @@ void pane_resize(struct pane *pane,
void pane_init(struct pane *p, pane_handler handle, pane_updater update);
void pane_title(struct pane *pane, const char *title, int highlight);
+void style_init(int style, int fg, int bg, int attr);
+void style_on(WINDOW *win, int style);
+void style_off(WINDOW *win, int style);
+
char *command_name_generator(const char *text, int state);
char *track_name_generator(const char *text, int state);
-int track_input(wint_t c);
-void track_vis(struct pane *pane, int sel);
-
int tag_input(wint_t c);
void tag_vis(struct pane *pane, int sel);
+int track_input(wint_t c);
+void track_vis(struct pane *pane, int sel);
+
int cmd_input(wint_t c);
void cmd_vis(struct pane *pane, int sel);
void main_input(wint_t c);
void main_vis(void);
+int usercmd_save(const char *args);
+
+
+const struct cmd cmds[] = {
+ { "save", usercmd_save },
+};
+
void
init(void)
@@ -138,6 +157,7 @@ init(void)
initscr();
cbreak();
noecho();
+ halfdelay(1);
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
start_color();
@@ -148,8 +168,12 @@ init(void)
history_init(&search_history);
history_init(&command_history);
- init_pair(STYLE_TITLE, COLOR_WHITE, COLOR_BLUE);
- init_pair(STYLE_PANE_SEP, COLOR_BLUE, COLOR_BLACK);
+ style_init(STYLE_DEFAULT, COLOR_WHITE, COLOR_BLACK, 0);
+ style_init(STYLE_TITLE, COLOR_WHITE, COLOR_BLUE, A_BOLD);
+ style_init(STYLE_PANE_SEP, COLOR_BLUE, COLOR_BLACK, 0);
+ style_init(STYLE_ITEM_SEL, COLOR_YELLOW, COLOR_BLACK, A_BOLD);
+ style_init(STYLE_ITEM_HOVER, COLOR_WHITE, COLOR_BLUE, 0);
+ style_init(STYLE_ITEM_HOVER_SEL, COLOR_YELLOW, COLOR_BLUE, A_BOLD);
pane_init(&pane_left, tag_input, tag_vis);
pane_init(&pane_right, track_input, track_vis);
@@ -157,12 +181,34 @@ init(void)
pane_sel = &pane_left;
+ datadir = getenv("TMUS_DATA");
+ ASSERT(datadir != NULL);
+
+ track_index = 0;
+ tag_index = 0;
+ tags_sel = LIST_HEAD;
+
+ // player = player_thread();
+
+ data_load();
+
atexit(cleanup);
}
void
cleanup(void)
{
+ int status;
+
+ // TODO stop player
+
+ data_save();
+
+ kill(player->pid, SIGTERM);
+ waitpid(player->pid, &status, 0);
+
+ // TODO free player
+
history_free(&search_history);
history_free(&command_history);
@@ -173,6 +219,83 @@ cleanup(void)
}
void
+data_load(void)
+{
+ struct dirent *ent;
+ struct tag *tag;
+ DIR *dir;
+
+ tags = LIST_HEAD;
+
+ dir = opendir(datadir);
+ ASSERT(dir != NULL);
+ while ((ent = readdir(dir))) {
+ if (ent->d_type != DT_DIR)
+ continue;
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
+
+ tag = malloc(sizeof(struct tag));
+ ASSERT(tag != NULL);
+ tag->fname = strdup(ent->d_name);
+ ASSERT(tag->fname != NULL);
+ tag->fpath = aprintf("%s/%s", datadir, ent->d_name);
+ ASSERT(tag->fpath != NULL);
+ tag->name = sanitized(tag->fname);
+ ASSERT(tag->name != NULL);
+ tag->link = LINK_EMPTY;
+ link_push_back(&tags, &tag->link);
+
+ tracks_load(tag);
+ }
+ closedir(dir);
+}
+
+void
+tracks_load(struct tag *tag)
+{
+ struct dirent *ent;
+ struct track *track;
+ struct tag_ref *tagref;
+ DIR *dir;
+
+ dir = opendir(tag->fpath);
+ ASSERT(dir != NULL);
+ while ((ent = readdir(dir))) {
+ if (ent->d_type != DT_REG)
+ continue;
+ if (!strcmp(ent->d_name, "."))
+ continue;
+ if (!strcmp(ent->d_name, ".."))
+ continue;
+
+ track = track_init(tag->fpath, ent->d_name);
+ tagref = malloc(sizeof(struct tag_ref));
+ ASSERT(tagref != NULL);
+ tagref->tag = tag;
+ tagref->link = LINK_EMPTY;
+ link_push_back(&track->tags, &tagref->link);
+
+ link_push_back(&tracks, &track->link);
+ }
+ closedir(dir);
+}
+
+void
+data_save(void)
+{
+
+}
+
+void
+tracks_save(struct tag *tag)
+{
+
+}
+
+void
resize(void)
{
int i;
@@ -223,91 +346,210 @@ pane_title(struct pane *pane, const char *title, int highlight)
{
wmove(pane->win, 0, 0);
- STYLE_ON(pane->win, TITLE);
- ATTR_ON(pane->win, A_BOLD);
+ style_on(pane->win, STYLE_TITLE);
if (highlight) ATTR_ON(pane->win, A_STANDOUT);
wprintw(pane->win, " %-*.*s", pane->w - 1, pane->w - 1, title);
if (highlight) ATTR_OFF(pane->win, A_STANDOUT);
- ATTR_OFF(pane->win, A_BOLD);
- STYLE_OFF(pane->win, TITLE);
+ style_off(pane->win, STYLE_TITLE);
}
-char *
-command_name_generator(const char *text, int reset)
+void
+style_init(int style, int fg, int bg, int attr)
{
- return NULL;
+ style_attrs[style] = attr;
+ init_pair(style, fg, bg);
+}
+
+void
+style_on(WINDOW *win, int style)
+{
+ ATTR_ON(win, COLOR_PAIR(style) | style_attrs[style]);
+}
+
+void
+style_off(WINDOW *win, int style)
+{
+ ATTR_OFF(win, COLOR_PAIR(style) | style_attrs[style]);
}
char *
-track_name_generator(const char *text, int reset)
+command_name_generator(const char *text, int reset)
{
static int index, len;
- char *name;
-
- if (cmd_mode != SEARCH) return NULL;
if (reset) {
index = 0;
len = strlen(text);
}
- if (index++ == 0 && !strncmp("hello", text, len))
- return strdup("hello");
-
- // TODO: iter over playlist
- // while ((name = track_names[list_index++])) {
- // if (strncmp(name, text, len) == 0) {
- // return strdup(name);
- // }
- // }
+ for (; index < ARRLEN(cmds); index++) {
+ if (!strncmp(cmds[index].name, text, len))
+ return strdup(cmds[index].name);
+ }
return NULL;
}
-void
-tag_init(void)
+char *
+track_name_generator(const char *text, int reset)
{
- /* TODO: load tags as directory names (excluding unsorted) */
+ static struct link *cur;
+ struct track *track;
+ static int len;
+
+ if (reset) {
+ cur = tracks.next;
+ len = strlen(text);
+ }
+
+ for (; cur; cur = cur->next) {
+ track = UPCAST(cur, struct track);
+ if (!strncmp(track->name, text, len))
+ return strdup(track->name);
+ }
+
+ return NULL;
}
int
tag_input(wint_t c)
{
+ struct link *cur;
+ struct tag *tag;
+
+ switch (c) {
+ case KEY_UP:
+ tag_index = MAX(0, tag_index - 1);
+ return 1;
+ case KEY_DOWN:
+ tag_index = MIN(list_len(&tags) - 1,
+ tag_index + 1);
+ return 1;
+ case KEY_SPACE:
+ cur = link_iter(tags.next, tag_index);
+ ASSERT(cur != NULL);
+ tag = UPCAST(cur, struct tag);
+ if (tagrefs_incl(&tags_sel, tag)) {
+ tagrefs_rm(&tags_sel, tag);
+ } else {
+ tagrefs_add(&tags_sel, tag);
+ }
+ return 1;
+ }
+
return 0;
}
void
tag_vis(struct pane *pane, int sel)
{
+ struct tag *tag, *tag2;
+ struct link *iter, *iter2;
+ int index, tsel;
+
werase(pane->win);
pane_title(pane, "Tags", sel);
-}
-void
-track_init(void)
-{
- /* TODO: for each tag director load track names */
+ index = 0;
+ for (iter = tags.next; iter; iter = iter->next) {
+ tag = UPCAST(iter, struct tag);
+ tsel = tagrefs_incl(&tags_sel, tag);
+
+ if (sel && index == tag_index && tsel)
+ style_on(pane->win, STYLE_ITEM_HOVER_SEL);
+ else if (sel && index == tag_index)
+ style_on(pane->win, STYLE_ITEM_HOVER);
+ else if (tsel)
+ style_on(pane->win, STYLE_ITEM_SEL);
+
+ wmove(pane->win, 1 + index, 0);
+ wprintw(pane->win, "%*.*s", pane->w, pane->w, tag->name);
+
+ if (index == tag_index && tsel)
+ style_off(pane->win, STYLE_ITEM_HOVER_SEL);
+ else if (index == tag_index)
+ style_off(pane->win, STYLE_ITEM_HOVER);
+ else if (tsel)
+ style_off(pane->win, STYLE_ITEM_SEL);
+ index++;
+ }
}
int
track_input(wint_t c)
{
+ struct link *link;
+ struct track *track;
+
+ switch (c) {
+ case KEY_UP:
+ track_index = MAX(0, track_index - 1);
+ return 1;
+ case KEY_DOWN:
+ track_index = MIN(list_len(&tracks) - 1,
+ track_index + 1);
+ return 1;
+ case KEY_ENTER:
+ link = link_iter(tracks.next, track_index);
+ ASSERT(link != NULL);
+ track = UPCAST(link, struct track);
+ if (track != track_sel) {
+ track_sel = track;
+ track_paused = 0;
+ } else {
+ track_paused ^= 1;
+ }
+ // if (!track_paused)
+ // track_play();
+ // else
+ // track_pause();
+ return 1;
+ }
+
return 0;
}
void
track_vis(struct pane *pane, int sel)
{
+ struct track *track;
+ struct link *iter;
+ int index;
+
werase(pane->win);
pane_title(pane, "Tracks", sel);
+
+ index = 0;
+ for (iter = tracks.next; iter; iter = iter->next) {
+ track = UPCAST(iter, struct track);
+
+ if (sel && index == track_index && track == track_sel)
+ style_on(pane->win, STYLE_ITEM_HOVER_SEL);
+ else if (sel && index == track_index)
+ style_on(pane->win, STYLE_ITEM_HOVER);
+ else if (track == track_sel)
+ style_on(pane->win, STYLE_ITEM_SEL);
+
+ wmove(pane->win, 1 + index, 0);
+ wprintw(pane->win, "%-*.*s", pane->w, pane->w, track->name);
+
+ if (sel && index == track_index && track == track_sel)
+ style_off(pane->win, STYLE_ITEM_HOVER_SEL);
+ else if (sel && index == track_index)
+ style_off(pane->win, STYLE_ITEM_HOVER);
+ else if (track == track_sel)
+ style_off(pane->win, STYLE_ITEM_SEL);
+
+ index++;
+ }
}
int
cmd_input(wint_t c)
{
- if (cmd_mode == EXECUTE) {
+ if (cmd_mode == IMODE_EXECUTE) {
history = &command_history;
completion = command_name_generator;
} else {
@@ -337,9 +579,10 @@ cmd_input(wint_t c)
case KEY_DOWN:
history_prev(history);
break;
- case '\n':
case KEY_ENTER:
history_submit(history);
+ if (!*history->cmd->buf)
+ pane_sel = NULL;
break;
case KEY_TAB:
break;
@@ -362,13 +605,17 @@ cmd_vis(struct pane *pane, int sel)
werase(pane->win);
if (track_sel)
- pane_title(pane, track_sel->title, track_paused);
+ pane_title(pane, track_sel->name, sel);
else
- pane_title(pane, "", 0);
+ pane_title(pane, "", sel);
+
+ static int i = 0;
+ wmove(pane->win, 1, 0);
+ wprintw(pane->win, "%i", i++);
if (sel || cmd_show) {
wmove(pane->win, 2, 0);
- waddch(pane->win, cmd_mode == SEARCH ? '/' : ':');
+ waddch(pane->win, cmd_mode == IMODE_SEARCH ? '/' : ':');
cmd = history->cmd;
wprintw(pane->win, "%-*.*ls", pane->w - 1, pane->w - 1, cmd->buf);
// TODO: if query != cmd, highlight query substr
@@ -401,11 +648,11 @@ main_input(wint_t c)
pane_sel = &pane_right;
break;
case ':':
- cmd_mode = EXECUTE;
+ cmd_mode = IMODE_EXECUTE;
pane_sel = &pane_bot;
break;
case '/':
- cmd_mode = SEARCH;
+ cmd_mode = IMODE_SEARCH;
pane_sel = &pane_bot;
break;
}
@@ -416,17 +663,24 @@ main_vis(void)
{
int i;
- STYLE_ON(stdscr, TITLE);
+ style_on(stdscr, STYLE_TITLE);
move(0, pane_left.ex);
addch(' ');
- STYLE_OFF(stdscr, TITLE);
+ style_off(stdscr, STYLE_TITLE);
- STYLE_ON(stdscr, PANE_SEP);
+ style_on(stdscr, STYLE_PANE_SEP);
for (i = pane_left.sy + 1; i < pane_left.ey; i++) {
move(i, pane_left.ex);
addch(ACS_VLINE);
}
- STYLE_OFF(stdscr, PANE_SEP);
+ style_off(stdscr, STYLE_PANE_SEP);
+}
+
+int
+usercmd_save(const char *args)
+{
+ data_save();
+ return 1;
}
int
@@ -441,7 +695,7 @@ main(int argc, const char **argv)
do {
if (c == KEY_RESIZE) {
resize();
- } else {
+ } else if (c != ERR) {
handled = 0;
if (pane_sel && pane_sel->active)
handled = pane_sel->handle(c);
diff --git a/player.c b/player.c
@@ -0,0 +1,113 @@
+#include "player.h"
+
+#include "portaudio.h"
+#include "sndfile.h"
+
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+static struct player player_static;
+struct player *player;
+
+struct player *
+player_thread()
+{
+ struct player *player;
+ pid_t pid;
+
+ player = mmap(&player_static, sizeof(struct player),
+ PROT_READ | PROT_WRITE, MAP_SHARED, -1, 0);
+ player->action = PLAYER_NONE;
+ player->resp = PLAYER_NOTSET;
+ player->alive = 1;
+
+ pid = fork();
+ if (!pid) {
+ player_main();
+ player->alive = 0;
+ exit(0);
+ }
+
+ player->pid = pid;
+
+ return player;
+}
+
+void
+player_main(void)
+{
+ PaStream *stream;
+ int status;
+
+ if (Pa_Initialize() != paNoError)
+ return;
+
+ while (player->action != PLAYER_EXIT) {
+ player->resp = PLAYER_NOTSET;
+ switch (player->action) {
+ case PLAYER_PLAY:
+ player->resp = player_play();
+ break;
+ case PLAYER_PAUSE:
+ player->resp = player_pause();
+ break;
+ case PLAYER_SKIP:
+ player->resp = player_skip();
+ break;
+ case PLAYER_PREV:
+ player->resp = player_prev();
+ break;
+ }
+ Pa_Sleep(100);
+ }
+
+ Pa_Terminate();
+}
+
+int
+player_alive(void)
+{
+ return player->alive && !kill(player->pid, 0);
+}
+
+void
+player_loadfile(const char *file)
+{
+ ASSERT(player_alive());
+ player_action(PLAYER_STOP);
+}
+
+void
+player_action(int action)
+{
+ ASSERT(player_alive());
+ player->action = action;
+}
+
+int
+player_play(void)
+{
+ return PLAYER_OK;
+}
+
+int
+player_pause(void)
+{
+ return PLAYER_OK;
+}
+
+int
+player_skip(void)
+{
+ return PLAYER_OK;
+}
+
+int
+player_prev(void)
+{
+ return PLAYER_OK;
+}
+
+
+
diff --git a/player.h b/player.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include "util.h"
+
+#include "sndfile.h"
+
+#include <signal.h>
+
+enum {
+ PLAYER_NONE,
+ PLAYER_PAUSE,
+ PLAYER_PLAY,
+ PLAYER_SKIP,
+ PLAYER_PREV,
+ PLAYER_STOP,
+ PLAYER_LOAD,
+ PLAYER_EXIT
+};
+
+enum {
+ PLAYER_NOTSET,
+ PLAYER_OK,
+ PLAYER_FAIL
+};
+
+struct player {
+ int action, resp;
+
+ int reload;
+ char *filepath;
+ SNDFILE *file;
+ SF_INFO info;
+
+ int sample_index;
+
+ int alive;
+ pid_t pid;
+};
+
+struct player *player_thread(void);
+
+void player_main(void);
+
+int player_alive(void);
+
+void player_loadfile(const char *path);
+void player_action(int action);
+
+int player_pause(void);
+int player_play(void);
+int player_prev(void);
+int player_skip(void);
+
+extern struct player *player;
diff --git a/tag.c b/tag.c
@@ -0,0 +1,53 @@
+#include "tag.h"
+#include "link.h"
+
+static struct link *
+tagrefs_ffind(struct link *head, struct tag *tag)
+{
+ struct link *iter;
+
+ for (iter = head->next; iter; iter = iter->next) {
+ if (UPCAST(iter, struct tag_ref)->tag == tag)
+ return iter;
+ }
+
+ return NULL;
+}
+
+int
+tagrefs_incl(struct link *head, struct tag *tag)
+{
+ struct link *ref;
+
+ ref = tagrefs_ffind(head, tag);
+ return ref != NULL;
+}
+
+void
+tagrefs_add(struct link *head, struct tag *tag)
+{
+ struct tag_ref *ref;
+
+ if (tagrefs_incl(head, tag))
+ return;
+
+ ref = malloc(sizeof(struct tag_ref));
+ ASSERT(ref != NULL);
+ ref->link = LINK_EMPTY;
+ ref->tag = tag;
+ link_push_back(head, &ref->link);
+}
+
+void
+tagrefs_rm(struct link *head, struct tag *tag)
+{
+ struct link *ref;
+ struct tag_ref *tagref;
+
+ ref = tagrefs_ffind(head, tag);
+ if (!ref) return;
+
+ tagref = UPCAST(ref, struct tag_ref);
+ link_pop(ref);
+ free(tagref);
+}
diff --git a/tag.h b/tag.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "link.h"
+#include "util.h"
+
+struct tag {
+ char *name;
+ char *fname, *fpath;
+
+ struct link link;
+};
+
+struct tag_ref {
+ struct tag *tag;
+
+ struct link link;
+};
+
+int tagrefs_incl(struct link *head, struct tag *tag);
+void tagrefs_add(struct link *head, struct tag *tag);
+void tagrefs_rm(struct link *head, struct tag *tag);
+
diff --git a/track.c b/track.c
@@ -0,0 +1,37 @@
+#include "track.h"
+
+#include <string.h>
+
+
+struct track *
+track_init(const char *dir, const char *file)
+{
+ struct track *track;
+
+ track = malloc(sizeof(struct track));
+
+ ASSERT(track != NULL);
+ track->fname = strdup(file);
+ ASSERT(track->fname != NULL);
+ track->fpath = aprintf("%s/%s", dir, file);
+ ASSERT(track->fpath != NULL);
+ track->name = sanitized(track->fname);
+ ASSERT(track->name != NULL);
+
+ // TODO track_load_info(track)
+ track->artist = NULL;
+ track->duration = 0;
+ track->link = LINK_EMPTY;
+
+ track->tags = LIST_HEAD;
+
+ return track;
+}
+
+void
+track_free(struct track *t)
+{
+ free(t->fname);
+ free(t->fpath);
+ free(t->name);
+}
diff --git a/track.h b/track.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "link.h"
+#include "util.h"
+
+
+struct track {
+ char *name;
+ char *artist;
+ float duration;
+ struct link tags;
+ char *fname, *fpath;
+
+ struct link link;
+};
+
+struct track_ref {
+ struct track *track;
+
+ struct link link;
+};
+
+
+struct track *track_init(const char *dir, const char *file);
+void track_free(struct track *t);
diff --git a/util.c b/util.c
@@ -2,11 +2,15 @@
#include "util.h"
+#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <wctype.h>
+static const char *allowed = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:,;-_(){}[]";
+
int
strnwidth(const char *s, int n)
{
@@ -41,3 +45,45 @@ assert(int cond, const char *file, int line, const char *condstr)
fprintf(stderr, "Assertion failed %s:%i (%s)", file, line, condstr);
exit(1);
}
+
+char *
+sanitized(const char *instr)
+{
+ const char *p;
+ char *clean;
+ int i;
+
+ clean = strdup(instr);
+ ASSERT(clean != NULL);
+ for (i = 0, p = instr; *p; p++) {
+ if (strchr(allowed, *p))
+ clean[i++] = *p;
+ }
+ ASSERT(i != 0);
+ clean[i] = '\0';
+
+ return clean;
+}
+
+char *
+aprintf(const char *fmtstr, ...)
+{
+ va_list ap, cpy;
+ size_t size;
+ char *str;
+
+ va_copy(cpy, ap);
+
+ va_start(ap, fmtstr);
+ size = vsnprintf(NULL, 0, fmtstr, ap);
+ va_end(ap);
+
+ str = malloc(size + 1);
+ ASSERT(str != NULL);
+
+ va_start(cpy, fmtstr);
+ vsnprintf(str, size + 1, fmtstr, cpy);
+ va_end(cpy);
+
+ return str;
+}
diff --git a/util.h b/util.h
@@ -10,3 +10,7 @@
int strnwidth(const char *s, int n);
void assert(int cond, const char *file, int line, const char *condstr);
+
+char *sanitized(const char *instr);
+
+char *aprintf(const char *fmtstr, ...);