Initial commit.
This commit is contained in:
commit
cc2c7993ab
22 changed files with 807 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Object files
|
||||
*.o
|
||||
|
||||
# Binary file
|
||||
pipeplayer
|
47
Makefile
Normal file
47
Makefile
Normal file
|
@ -0,0 +1,47 @@
|
|||
# pipeplayer - simple music player
|
||||
|
||||
.POSIX:
|
||||
|
||||
include config.mk
|
||||
|
||||
SRC = pipeplayer.c dirname.c player.c playlist.c list.c threads.c pipe.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
all: options $(NAME)
|
||||
|
||||
options:
|
||||
@echo $(NAME) build options:
|
||||
@echo "CFLAGS = $(CFLAGS)"
|
||||
@echo "LDFLAGS = $(LDFLAGS)"
|
||||
@echo "CC = $(CC)"
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
log.h: config.h loglevels.h
|
||||
threads.o: commands.h playlist.h player.h log.h threads.h pipe.h
|
||||
playlist.o: playlist.h dirname.h list.h log.h
|
||||
player.o: player.h config.h log.h
|
||||
pipeplayer.o: player.h playlist.h threads.h pipe.h
|
||||
pipe.o: pipe.h
|
||||
list.o: list.h
|
||||
|
||||
$(OBJ): config.mk
|
||||
|
||||
$(NAME): $(OBJ)
|
||||
$(CC) -o $@ $(LDFLAGS) $(OBJ)
|
||||
|
||||
clean:
|
||||
rm -f $(NAME) $(OBJ)
|
||||
|
||||
install: $(NAME)
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||
cp -f $(NAME) $(DESTDIR)$(PREFIX)/bin
|
||||
chmod 755 $(DESTDIR)$(PREFIX)/bin/$(NAME)
|
||||
mkdir -p $(DESTDIR)$(MANPREFIX)/man1
|
||||
sed "s/VERSION/$(VERSION)/g" < $(NAME).1 > $(DESTDIR)$(MANPREFIX)/man1/$(NAME).1
|
||||
chmod 644 $(DESTDIR)$(MANPREFIX)/man1/$(NAME).1
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/$(NAME)
|
||||
rm -f $(DESTDIR)$(MANPREFIX)/man1/$(NAME).1
|
74
README.md
Normal file
74
README.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
# Pipeplayer
|
||||
|
||||
Pipeplayer is a simple music daemon. It uses libmpg123 to decode files and libao to play sound. You can control it with named pipe (FIFO).
|
||||
|
||||
## Before using this
|
||||
|
||||
This program requires *playlist* file which is plain text of songs in the same directory.
|
||||
For example, you have folder with this files: `song1.mp3`, `song_2.mp3`, `4.mp3` and `56.mp3`.
|
||||
Your playlist file shoul'd look like:
|
||||
```
|
||||
song1.mp3
|
||||
song_2.mp3
|
||||
4.mp3
|
||||
56.mp3
|
||||
```
|
||||
**Tip**: cd into folder with music and run command `ls -1N | tee playlist` to automatically generate playlist file. Note that order may differ.
|
||||
|
||||
## Usage
|
||||
|
||||
First of all, you need to create named pipe (FIFO). It can be done by running following command:
|
||||
```
|
||||
mkfifo ~/.cache/.pipeplayer
|
||||
```
|
||||
|
||||
Then, you can start daemon either during start of your desktop environment or window manager or even in tty with this command:
|
||||
```
|
||||
pipeplayer ~/.cache/.pipeplayer
|
||||
```
|
||||
Note: add `&` at the end of command to run process in background.
|
||||
|
||||
Now you can send commands to created named pipe like this:
|
||||
```
|
||||
echo l$HOME/Music/playlist > ~/.cache/.pipeplayer
|
||||
```
|
||||
|
||||
You can find all commands in man page, after installation you can run: `man pipeplayer`
|
||||
|
||||
# Installation
|
||||
|
||||
You need a few things:
|
||||
* git
|
||||
* C compiler, such as **gcc**, **clang** or **tcc**
|
||||
* make
|
||||
* libmpg123
|
||||
* libao
|
||||
|
||||
Install that stuff, clone this repository, cd into it and simply run make and sudo (or doas) make install:
|
||||
|
||||
```
|
||||
git clone https://git.disroot.org/elijah.dev/pipeplayer.git
|
||||
cd pipeplayer
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
If you prefer gcc, then run `make CC=gcc` instead of `make`.
|
||||
Also, if you prefer clang, run `make CC=clang STRIPFLAG=`
|
||||
|
||||
Here some tips for installing dependencies on some distros:
|
||||
|
||||
### Archlinux and arch-based distros
|
||||
```
|
||||
pacman -Syu --needed tcc mpg123 libao
|
||||
```
|
||||
|
||||
Guides for other distibutions will appear in future. If you know one, feel free to add.
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Add pause command
|
||||
- [ ] Add navigation inside song
|
||||
|
||||
## Bugs
|
||||
|
||||
If you have found bugs, you can open issue or contact me via email elijah.dev@disroot.org
|
10
commands.h
Normal file
10
commands.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef __COMMANDS_H__
|
||||
#define __COMMANDS_H__
|
||||
|
||||
#define EXIT 'e'
|
||||
#define LOAD 'l'
|
||||
#define FORWARD 'f'
|
||||
#define BACKWARD 'b'
|
||||
#define NEWLINE '\n'
|
||||
|
||||
#endif /* __COMMANDS_H__ */
|
16
config.h
Normal file
16
config.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
#define BITS 8
|
||||
|
||||
// Logging options
|
||||
#include "loglevels.h"
|
||||
|
||||
/* 0 - off colors
|
||||
* 1 - on colors */
|
||||
#define LOGGING_COLORS 1
|
||||
|
||||
// Levels (see log.h)
|
||||
#define LOGGING_LEVEL INFO
|
||||
|
||||
#endif /* __CONFIG_H__ */
|
19
config.mk
Normal file
19
config.mk
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Customize below to fit your system
|
||||
PREFIX = /usr/local
|
||||
MANPREFIX = $(PREFIX)/share/man
|
||||
|
||||
# Name of program
|
||||
VERSION = 1.0.0
|
||||
NAME = pipeplayer
|
||||
|
||||
# Compiler
|
||||
CC = tcc
|
||||
|
||||
PKG_CONFIG = pkg-config
|
||||
|
||||
LIBS = -lpthread `$(PKG_CONFIG) -libs libmpg123 ao`
|
||||
|
||||
# Flags
|
||||
STRIPFLAG = -s
|
||||
CFLAGS = -O2 -Wall
|
||||
LDFLAGS = -O2 $(STRIPFLAG) $(LIBS)
|
31
dirname.c
Normal file
31
dirname.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* Put dirname with slash in buffer
|
||||
* Return length of buffer */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
size_t
|
||||
dirname(const char *path, char **buffer_ptr)
|
||||
{
|
||||
size_t index = 0, slash_index = 0;
|
||||
|
||||
// Find slash
|
||||
for (; path[index] != '\0'; index++)
|
||||
if (path[index] == '/')
|
||||
slash_index = index;
|
||||
|
||||
// If not slashes or one at the beginning, then / is dirname
|
||||
if (slash_index == 0) {
|
||||
*buffer_ptr = (char *) malloc(2);
|
||||
strcpy(*buffer_ptr, "/");
|
||||
return (size_t)1;
|
||||
}
|
||||
|
||||
// Copy string
|
||||
slash_index++;
|
||||
*buffer_ptr = (char *) malloc(slash_index + 1);
|
||||
strncpy(*buffer_ptr, path, slash_index);
|
||||
(*buffer_ptr)[slash_index] = '\0';
|
||||
return slash_index;
|
||||
}
|
8
dirname.h
Normal file
8
dirname.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef __DIRNAME_H__
|
||||
#define __DIRNAME_H__
|
||||
|
||||
/* Get directory from path
|
||||
* Return length, store in buffer */
|
||||
size_t dirname(const char *path, char **buffer);
|
||||
|
||||
#endif /* __DIRNAME_H__ */
|
30
list.c
Normal file
30
list.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <stdlib.h>
|
||||
#include "list.h"
|
||||
|
||||
node_t *
|
||||
new_node(node_t *previous)
|
||||
{
|
||||
node_t *node = (node_t *) malloc(sizeof(node_t));
|
||||
node->next = NULL;
|
||||
if (previous != NULL)
|
||||
previous->next = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
void
|
||||
free_list(node_t *root)
|
||||
{
|
||||
while (root != NULL) {
|
||||
node_t *previous = root;
|
||||
root = root->next;
|
||||
free(previous);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
node_pop_first(node_t **root)
|
||||
{
|
||||
node_t *current = *root;
|
||||
*root = current->next;
|
||||
free(current);
|
||||
}
|
15
list.h
Normal file
15
list.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef __LIST_H__
|
||||
#define __LIST_H__
|
||||
|
||||
// Linked list implementation
|
||||
|
||||
typedef struct node {
|
||||
char *str;
|
||||
struct node *next;
|
||||
} node_t;
|
||||
|
||||
node_t *new_node(node_t *previous);
|
||||
void free_list(node_t *root);
|
||||
void node_pop_first(node_t **root);
|
||||
|
||||
#endif /* __LIST_H__ */
|
43
log.h
Normal file
43
log.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef __LOG_H__
|
||||
#define __LOG_H__
|
||||
|
||||
#include "loglevels.h"
|
||||
#include "config.h"
|
||||
|
||||
// Default logging level is error
|
||||
#ifndef LOGGING_LEVEL
|
||||
#define LOGGING_LEVEL ERROR
|
||||
#endif /* Logging level */
|
||||
|
||||
// Colors
|
||||
#if LOGGING_COLORS == 1
|
||||
#define __LOGGING_ERROR_MESSAGE "[91m[ERROR](B[m "
|
||||
#define __LOGGING_INFO_MESSAGE "[92m[INFO](B[m "
|
||||
#define __LOGGING_DEBUG_MESSAGE "[94m[DEBUG](B[m "
|
||||
#else
|
||||
#define __LOGGING_ERROR_MESSAGE "[ERROR] "
|
||||
#define __LOGGING_INFO_MESSAGE "[INFO] "
|
||||
#define __LOGGING_DEBUG_MESSAGE "[DEBUG] "
|
||||
#endif /* Colors */
|
||||
|
||||
// Logging macro and include of stdio
|
||||
#if LOGGING_LEVEL >= ERROR
|
||||
#include <stdio.h>
|
||||
#define LOG_ERROR(...) printf(__LOGGING_ERROR_MESSAGE); printf(__VA_ARGS__); puts("");
|
||||
#else
|
||||
#define LOG_ERROR(...)
|
||||
#endif
|
||||
|
||||
#if LOGGING_LEVEL >= INFO
|
||||
#define LOG_INFO(...) printf(__LOGGING_INFO_MESSAGE); printf(__VA_ARGS__); puts("");
|
||||
#else
|
||||
#define LOG_INFO(...)
|
||||
#endif
|
||||
|
||||
#if LOGGING_LEVEL >= DEBUG
|
||||
#define LOG_DEBUG(...) printf(__LOGGING_DEBUG_MESSAGE); printf(__VA_ARGS__); puts("");
|
||||
#else
|
||||
#define LOG_DEBUG(...)
|
||||
#endif
|
||||
|
||||
#endif /* __LOG_H__ */
|
14
loglevels.h
Normal file
14
loglevels.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef __LOG_LEVELS_H__
|
||||
#define __LOG_LEVELS_H__
|
||||
|
||||
/* Logging levels
|
||||
* NOLOG means no log messages at all
|
||||
* ERROR is error messages
|
||||
* INFO provides error and info messages
|
||||
* DEBUG provides all log messages */
|
||||
#define NOLOG 0
|
||||
#define ERROR 1
|
||||
#define INFO 2
|
||||
#define DEBUG 3
|
||||
|
||||
#endif /* __LOG_LEVELS_H__ */
|
17
pipe.c
Normal file
17
pipe.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pipe.h"
|
||||
|
||||
size_t
|
||||
pipe_readline(char **buffer_ptr, char *pipe_path)
|
||||
{
|
||||
size_t len;
|
||||
char *line = NULL;
|
||||
FILE *fd = fopen(pipe_path, "r");
|
||||
size_t read = getline(&line, &len, fd);
|
||||
line[--read] = '\0';
|
||||
fclose(fd);
|
||||
*buffer_ptr = line;
|
||||
return read;
|
||||
}
|
8
pipe.h
Normal file
8
pipe.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef __PIPE_H__
|
||||
#define __PIPE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
size_t pipe_readline(char **buffer_ptr, char *pipe_path);
|
||||
|
||||
#endif /* __PIPE_H__ */
|
52
pipeplayer.1
Normal file
52
pipeplayer.1
Normal file
|
@ -0,0 +1,52 @@
|
|||
.TH PIPEPLAYER 1 pipeplayer\-VERSION
|
||||
.SH NAME
|
||||
pipeplayer \- simple music daemon
|
||||
.SH SYNOPSIS
|
||||
.B pipeplayer
|
||||
/path/to/pipe
|
||||
.SH DESCRIPTION
|
||||
.B pipeplayer
|
||||
is simple music daemon controlled with named pipe. It uses libmpg123 to decode files and libao to play sound.
|
||||
.SH CONTROL
|
||||
.TP
|
||||
.B l/path
|
||||
load playlist from /path.
|
||||
.TP
|
||||
.B e
|
||||
stop pipeplayer.
|
||||
.TP
|
||||
.B b
|
||||
play previous song.
|
||||
.TP
|
||||
.B f
|
||||
play next song.
|
||||
.SH LOGGING
|
||||
You can change LOGGING_LEVEL at
|
||||
.B config.h
|
||||
to one of the following values:
|
||||
.TP
|
||||
.B NOLOG
|
||||
No logs.
|
||||
.TP
|
||||
.B ERROR
|
||||
Show only error messages.
|
||||
.TP
|
||||
.B INFO
|
||||
Show some information messages in addition to error messages.
|
||||
.TP
|
||||
.B DEBUG
|
||||
Show all log messages.
|
||||
.TP
|
||||
Then you need to recompile pipeplayer.
|
||||
.SH AUTHORS
|
||||
Written by Elijah <elijah.dev@disroot.org>
|
||||
.TP
|
||||
.B Git repository:
|
||||
https://git.disroot.org/elijah.dev/pipeplayer.git
|
||||
.SH LICENSE
|
||||
See the LICENSE file for the terms of redistribution.
|
||||
.SH SEE ALSO
|
||||
.BR mpg123 (1),
|
||||
.BR libao.conf (5)
|
||||
.SH BUGS
|
||||
If you have found bugs, you can open issue on git page or contact me via email.
|
60
pipeplayer.c
Normal file
60
pipeplayer.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "player.h"
|
||||
#include "playlist.h"
|
||||
#include "threads.h"
|
||||
#include "pipe.h"
|
||||
|
||||
// Function on Ctrl-C
|
||||
void
|
||||
sigint_handler(int _)
|
||||
{
|
||||
puts("Use 'e' command to exit");
|
||||
}
|
||||
|
||||
// Entry point
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
// Usage information
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s [FIFO]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (access(argv[1], F_OK) != 0) {
|
||||
printf("Cannot access file %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// mpg player
|
||||
struct mpg_player player, *player_ptr = &player;
|
||||
player.ao.need_close_device = false;
|
||||
|
||||
// playlist
|
||||
struct playlist playlist = {
|
||||
.need_free = false,
|
||||
.need_cancel = false,
|
||||
.index = 0,
|
||||
.length = 0,
|
||||
.path = NULL,
|
||||
}, *playlist_ptr = &playlist;
|
||||
|
||||
// Set up Ctrl-C handler
|
||||
signal(SIGINT, sigint_handler);
|
||||
|
||||
// Initialize player
|
||||
init_player(player_ptr);
|
||||
|
||||
// Create and join input thread
|
||||
run_threads(player_ptr, playlist_ptr, argv[1]);
|
||||
|
||||
// Clear player and playlist
|
||||
free_player(player_ptr);
|
||||
free_playlist(playlist_ptr);
|
||||
|
||||
return 0;
|
||||
}
|
76
player.c
Normal file
76
player.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include <mpg123.h>
|
||||
#include <ao/ao.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "player.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
void
|
||||
init_player(struct mpg_player *player)
|
||||
{
|
||||
LOG_DEBUG("Initializing player");
|
||||
|
||||
// Audio output
|
||||
ao_initialize();
|
||||
player->ao.driver = ao_default_driver_id();
|
||||
// Mpg123
|
||||
mpg123_init();
|
||||
player->handler = mpg123_new(NULL, &player->error);
|
||||
player->buffer_size = mpg123_outblock(player->handler);
|
||||
player->buffer = (char*) malloc(player->buffer_size * sizeof(unsigned char));
|
||||
}
|
||||
|
||||
void
|
||||
free_player(struct mpg_player *player)
|
||||
{
|
||||
LOG_DEBUG("Clearing memory of player");
|
||||
|
||||
free(player->buffer);
|
||||
|
||||
if (player->ao.need_close_device)
|
||||
ao_close(player->ao.device);
|
||||
|
||||
mpg123_close(player->handler);
|
||||
mpg123_delete(player->handler);
|
||||
mpg123_exit();
|
||||
|
||||
ao_shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
play(struct mpg_player *player, const char *path)
|
||||
{
|
||||
/* Open the file and get the decoding format */
|
||||
LOG_INFO("Playing %s", path);
|
||||
|
||||
struct audio_output *ao = &player->ao;
|
||||
ao_sample_format *format = &ao->format;
|
||||
|
||||
mpg123_open(player->handler, path);
|
||||
mpg123_getformat(
|
||||
player->handler,
|
||||
&ao->rate,
|
||||
&ao->channels,
|
||||
&ao->encoding
|
||||
);
|
||||
|
||||
/* Set the output format and open the output device */
|
||||
format->bits = mpg123_encsize(ao->encoding) * BITS;
|
||||
format->rate = ao->rate;
|
||||
format->channels = ao->channels;
|
||||
format->byte_format = AO_FMT_NATIVE;
|
||||
format->matrix = 0;
|
||||
ao->device = ao_open_live(ao->driver, format, NULL);
|
||||
ao->need_close_device = true;
|
||||
|
||||
/* Decode and play */
|
||||
while (mpg123_read(
|
||||
player->handler,
|
||||
player->buffer,
|
||||
player->buffer_size,
|
||||
&player->done
|
||||
) == MPG123_OK)
|
||||
ao_play(ao->device, player->buffer, player->done);
|
||||
}
|
30
player.h
Normal file
30
player.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef __PLAYER_H__
|
||||
#define __PLAYER_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <mpg123.h>
|
||||
#include <ao/ao.h>
|
||||
|
||||
struct audio_output {
|
||||
ao_device *device;
|
||||
bool need_close_device;
|
||||
ao_sample_format format;
|
||||
int channels, encoding;
|
||||
int driver;
|
||||
long rate;
|
||||
};
|
||||
|
||||
struct mpg_player {
|
||||
mpg123_handle *handler;
|
||||
char *buffer;
|
||||
size_t buffer_size, done;
|
||||
int error;
|
||||
struct audio_output ao;
|
||||
};
|
||||
|
||||
void init_player(struct mpg_player *);
|
||||
void free_player(struct mpg_player *);
|
||||
void play(struct mpg_player *, const char *path);
|
||||
|
||||
#endif /* __PLAYER_H__ */
|
93
playlist.c
Normal file
93
playlist.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "playlist.h"
|
||||
#include "dirname.h"
|
||||
#include "list.h"
|
||||
#include "log.h"
|
||||
|
||||
void
|
||||
free_playlist(struct playlist *playlist)
|
||||
{
|
||||
if (!playlist->need_free)
|
||||
return;
|
||||
|
||||
LOG_DEBUG("Clearing playlist");
|
||||
|
||||
// Clear array items
|
||||
for (playlist->index = 0; playlist->index < playlist->length; playlist->index++)
|
||||
free(playlist->array[playlist->index]);
|
||||
|
||||
// Clear array
|
||||
free(playlist->array);
|
||||
|
||||
// Clear playlist dir
|
||||
free(playlist->dir);
|
||||
}
|
||||
|
||||
void
|
||||
load_playlist(struct playlist *playlist)
|
||||
{
|
||||
FILE *fp;
|
||||
ssize_t read;
|
||||
size_t length, len, count = 0;
|
||||
char *line = NULL;
|
||||
|
||||
// Clear previous playlist
|
||||
free_playlist(playlist);
|
||||
|
||||
LOG_INFO("Loading playlist from %s", playlist->path);
|
||||
|
||||
// Read file setup
|
||||
fp = fopen(playlist->path, "r");
|
||||
playlist->index = 0;
|
||||
|
||||
// Return if cannot open file or first line is empty
|
||||
if (fp == NULL) {
|
||||
LOG_ERROR("Cannot load playlist from %s", playlist->path);
|
||||
playlist->need_free = false;
|
||||
playlist->length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get dirname and length of it
|
||||
length = dirname(playlist->path, &playlist->dir);
|
||||
|
||||
node_t *list = new_node(NULL),
|
||||
*current = list;
|
||||
|
||||
// Read lines
|
||||
while ((read = getline(&line, &len, fp)) != EOF) {
|
||||
// "read" is length of string
|
||||
current = new_node(current);
|
||||
line[read - 1] = '\0';
|
||||
current->str = (char *) malloc(read + length);
|
||||
strncpy(current->str, playlist->dir, length);
|
||||
strncpy(current->str + length, line, read);
|
||||
count++;
|
||||
}
|
||||
|
||||
// Set playlist values
|
||||
playlist->need_free = true;
|
||||
playlist->length = count;
|
||||
|
||||
// Allocate array
|
||||
playlist->array = (char **) malloc(count * sizeof(char *));
|
||||
|
||||
// Copy strings (pointers) from list to array
|
||||
current = list;
|
||||
for (size_t index = 0; index < count; index++) {
|
||||
current = current->next;
|
||||
playlist->array[index] = current->str;
|
||||
}
|
||||
|
||||
// Clear list
|
||||
free_list(list);
|
||||
|
||||
// Close file and free line
|
||||
fclose(fp);
|
||||
if (line)
|
||||
free(line);
|
||||
}
|
16
playlist.h
Normal file
16
playlist.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef __PLAYLIST_H__
|
||||
#define __PLAYLIST_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct playlist {
|
||||
char *path, *dir, **array;
|
||||
size_t index, length;
|
||||
bool need_free, need_cancel;
|
||||
};
|
||||
|
||||
void free_playlist(struct playlist *);
|
||||
void load_playlist(struct playlist *);
|
||||
|
||||
#endif /* __PLAYLIST_H__ */
|
123
threads.c
Normal file
123
threads.c
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "commands.h"
|
||||
#include "playlist.h"
|
||||
#include "player.h"
|
||||
#include "log.h"
|
||||
#include "threads.h"
|
||||
#include "pipe.h"
|
||||
|
||||
void
|
||||
run_threads(struct mpg_player *player, struct playlist *playlist, char *pipe_path)
|
||||
{
|
||||
pthread_t thread_player = {0}, thread_handler = {0};
|
||||
|
||||
struct thread_arg arg = {
|
||||
.player = player,
|
||||
.playlist = playlist,
|
||||
.pipe_path = pipe_path,
|
||||
.thread_player = &thread_player
|
||||
};
|
||||
|
||||
pthread_create(&thread_handler, NULL, input_thread, (void *)&arg);
|
||||
pthread_join(thread_handler, NULL);
|
||||
}
|
||||
|
||||
void *
|
||||
play_thread(void *vargp)
|
||||
{
|
||||
struct thread_arg *arg = (struct thread_arg *)vargp;
|
||||
struct playlist *playlist = arg->playlist;
|
||||
struct mpg_player *player = arg->player;
|
||||
|
||||
LOG_DEBUG("Starting player thread");
|
||||
LOG_DEBUG("playlist->path = %s", playlist->path);
|
||||
LOG_DEBUG("playlist->index = %lu", playlist->index);
|
||||
LOG_DEBUG("playlist->length = %lu", playlist->length);
|
||||
|
||||
playlist->need_cancel = true;
|
||||
|
||||
for (; playlist->index < playlist->length; playlist->index++)
|
||||
play(player, playlist->array[playlist->index]);
|
||||
|
||||
playlist->need_cancel = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_restart_playlist_thread(struct playlist *playlist, void *vargp)
|
||||
{
|
||||
struct thread_arg *arg = (struct thread_arg *)vargp;
|
||||
pthread_t *thread_player = arg->thread_player;
|
||||
|
||||
LOG_DEBUG("Killing player thread");
|
||||
|
||||
if (playlist->need_cancel)
|
||||
pthread_cancel(*thread_player);
|
||||
pthread_create(thread_player, NULL, play_thread, vargp);
|
||||
}
|
||||
|
||||
void *
|
||||
input_thread(void *vargp)
|
||||
{
|
||||
size_t length;
|
||||
char *command, **command_ptr = (char **) malloc(sizeof(char *));
|
||||
bool run = true;
|
||||
|
||||
struct thread_arg *arg = (struct thread_arg *)vargp;
|
||||
struct playlist *playlist = arg->playlist;
|
||||
char *pipe_path = arg->pipe_path;
|
||||
|
||||
while (run) {
|
||||
length = pipe_readline(command_ptr, pipe_path);
|
||||
command = *command_ptr;
|
||||
LOG_DEBUG("Command: %s", command);
|
||||
|
||||
switch (*command) {
|
||||
case EXIT:
|
||||
case EOF:
|
||||
LOG_DEBUG("Case: EXIT | EOF");
|
||||
if (playlist->need_cancel)
|
||||
pthread_cancel(*arg->thread_player);
|
||||
run = false;
|
||||
break;
|
||||
|
||||
case LOAD:
|
||||
LOG_DEBUG("Case: LOAD");
|
||||
playlist->path = malloc(length);
|
||||
strcpy(playlist->path, command + 1);
|
||||
load_playlist(playlist);
|
||||
_restart_playlist_thread(playlist, vargp);
|
||||
break;
|
||||
|
||||
case FORWARD:
|
||||
LOG_DEBUG("Case: FORWARD");
|
||||
playlist->index = (playlist->index + 1) % playlist->length;
|
||||
_restart_playlist_thread(playlist, vargp);
|
||||
break;
|
||||
|
||||
case BACKWARD:
|
||||
LOG_DEBUG("Case: BACKWARD");
|
||||
playlist->index = (playlist->index + playlist->length - 1) % playlist->length;
|
||||
_restart_playlist_thread(playlist, vargp);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown command: %s\n", command);
|
||||
}
|
||||
|
||||
// Clear command
|
||||
if (command != NULL)
|
||||
free(command);
|
||||
}
|
||||
|
||||
// Clear command pointer
|
||||
free(command_ptr);
|
||||
|
||||
return 0;
|
||||
}
|
20
threads.h
Normal file
20
threads.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef __THREADS_H__
|
||||
#define __THREADS_H__
|
||||
|
||||
#include <pthread.h>
|
||||
#include "player.h"
|
||||
#include "playlist.h"
|
||||
|
||||
struct thread_arg {
|
||||
struct mpg_player *player;
|
||||
struct playlist *playlist;
|
||||
pthread_t *thread_player;
|
||||
char *pipe_path;
|
||||
};
|
||||
|
||||
// Thread function
|
||||
void run_threads(struct mpg_player *player, struct playlist *playlist, char *pipe_path);
|
||||
void *play_thread(void *vargp);
|
||||
void *input_thread(void *vargp);
|
||||
|
||||
#endif /* __THREADS_H__ */
|
Loading…
Reference in a new issue