change license from GPLv3 to GPLv3 or later
[mmq.git] / input.c
blobe1e3bcd6ac54e3cff2665cf7951e623f66d0b8a3
1 #include "mmq.h"
2 #include <sys/mman.h>
4 /* all mmq code to read audio files is here */
6 /* we only open one file at a time */
7 static int fd = -1;
8 static struct stat sb;
9 static void *mm;
11 /* current (early 2010) Linux doesn't handle POSIX_FADV_NOREUSE */
12 static int force_noreuse
13 #if defined(__linux__)
14 = 1
15 #endif
18 #ifndef SIZE_T_MAX
19 # define SIZE_T_MAX ((size_t)-1)
20 #endif
22 #ifndef PAGE_SIZE
23 # if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
24 # define PAGE_SIZE (4096)
25 # else
26 # define PAGE_SIZE (sysconf(_SC_PAGESIZE))
27 # endif
28 #endif
29 #define PAGE_MASK (~(PAGE_SIZE - 1))
30 #define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK)
33 * Linux madvise(2) doesn't map exactly to posix_madvise for DONTNEED,
34 * but the difference does not matter for read-only mappings.
36 #ifdef __linux__
37 # define madv_dontneed(addr,len) madvise(addr,len,MADV_DONTNEED)
38 #else
39 # define madv_dontneed(addr,len) posix_madvise(addr,len,POSIX_MADV_DONTNEED)
40 #endif
42 static void init_readahead(off_t offset)
44 /* no error checking here, these are only optimizations */
45 (void)posix_fadvise(fd, offset, sb.st_size, POSIX_FADV_NOREUSE);
46 (void)posix_fadvise(fd, offset, sb.st_size, POSIX_FADV_SEQUENTIAL);
49 int input_open(const char *path)
51 const char *err;
52 int flags = O_RDONLY | O_NOATIME;
54 sb.st_size = -1;
56 retry:
57 fd = open(path, flags);
58 if (fd < 0) {
59 if (flags & O_NOATIME) {
60 flags = O_RDONLY;
61 goto retry;
63 err = "open";
64 } else if (fstat(fd, &sb) < 0) {
65 err = "fstat";
66 } else {
67 if (g_seek_msec == 0)
68 init_readahead(0);
69 return 0;
72 warn("%s: %s\n", err, strerror(errno));
73 fd = -1;
74 return -1;
78 * this may only be called once per song
79 * mmq will never be able to seek once playback is started
80 * (frontends that want to can implement this feature trivially)
82 off_t input_seek(off_t offset, int whence)
84 off_t off = lseek(fd, offset, whence);
86 if (off < 0) {
87 if (errno != ESPIPE) {
88 warn("lseek: %s\n", strerror(errno));
89 g_state = STATE_NEXT;
91 } else {
92 init_readahead(off);
95 return off;
98 off_t input_size(void)
100 if (sb.st_size < 0)
101 g_state = STATE_NEXT;
102 return sb.st_size;
106 * FLAC/Tremor currently require a read()-like interface, we use read()
107 * instead of mmap()+memcpy() since setting up and maintaining memory
108 * mappings are still expensive and generally outweigh the cost of the
109 * copying and extra syscalls.
111 ssize_t input_read(void *buf, size_t count)
113 ssize_t r;
114 if (likely(g_state == STATE_PLAY)) {
115 r = read(fd, buf, count);
116 if (likely(r >= 0))
117 return r;
119 warn("read: %s\n", strerror(errno));
120 g_state = STATE_NEXT;
121 return r;
122 } else if (g_state == STATE_NEXT) {
123 return -1;
125 assert(0 && "unknown g_state, should never get here");
129 * mmap won't work on MMU-less machines since they can't mmap, but since
130 * only MAD uses this for MP3, and MP3s are reasonably small maybe we
131 * could just slurp the whole thing into memory (and refuse to play MP3s
132 * we can't slurp).
134 /* TODO: seek awareness */
135 void *input_mmap(size_t *size)
137 off_t length;
139 /* already mmap-ed, consider this an EOF */
140 if (mm)
141 return NULL;
143 length = PAGE_ALIGN(sb.st_size);
144 if (length > SIZE_T_MAX) {
145 warn("file too large for mmap\n");
146 goto err;
149 mm = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
150 if (mm == MAP_FAILED) {
151 warn("mmap: %s\n", strerror(errno));
152 goto err;
155 *size = sb.st_size;
156 (void)posix_madvise(mm, length, POSIX_MADV_SEQUENTIAL);
158 return mm;
159 err:
160 *size = -1;
161 g_state = STATE_NEXT;
162 return NULL;
165 void input_close(void)
167 if (mm) {
168 (void)madv_dontneed(mm, PAGE_ALIGN(sb.st_size));
169 if (munmap(mm, sb.st_size) < 0)
170 warn("munmap: %s\n", strerror(errno));
171 mm = NULL;
173 if (force_noreuse)
174 (void)posix_fadvise(fd, 0, sb.st_size, POSIX_FADV_DONTNEED);
175 if (close(fd) < 0)
176 warn("close: %s\n", strerror(errno));
178 fd = -1;