2 * Copyright (C) 2008 Mark Hills <mark@pogo.org.uk>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * version 2, as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License version 2 for more details.
13 * You should have received a copy of the GNU General Public License
14 * version 2 along with this program; if not, write to the Free
15 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
28 #include <sys/types.h>
33 #include "track_cache.h"
35 #define LOCK(tr) pthread_mutex_lock(&(tr)->mx)
36 #define UNLOCK(tr) pthread_mutex_unlock(&(tr)->mx)
38 static guint average_blocks
= 0;
39 static guint block_allocation_count
= 0;
41 static guint
get_average_blocks(const guint currentBlockCount
)
43 guint new_block_allocation_count
= block_allocation_count
+ 1;
44 average_blocks
= ((float)block_allocation_count
/ (float)new_block_allocation_count
* (float)(average_blocks
)) + ((float)currentBlockCount
/ (float)new_block_allocation_count
);
45 block_allocation_count
= new_block_allocation_count
;
46 return average_blocks
;
49 /* Start the importer process. On completion, pid and fd are set */
51 static int start_import(struct track_t
*tr
, const char *path
)
53 tr
->cache
= cached_track_init(path
);
55 //try to reserve cache file
56 tr
->cache_read
= cached_track_read(tr
->cache
);
59 tr
->fd
= cache_read_get_fd(tr
->cache_read
);
64 if(pipe(pstdout
) == -1) {
75 } else if(tr
->pid
== 0) { /* child */
78 dup2(pstdout
[1], STDOUT_FILENO
);
81 if(execl(tr
->importer
, "import", path
, NULL
) == -1) {
90 if(fcntl(tr
->fd
, F_SETFL
, O_NONBLOCK
) == -1) {
105 guint
track_get_used_blocks(const struct track_t
* tr
)
107 //double cast is necessary here because it
108 //otherwise converts the argument to int
109 return ceil((double)tr
->bytes
/ BYTES_PER_PCM_BLOCK
);
112 guint
track_get_last_block_used_bytes(const struct track_t
* tr
)
114 return tr
->bytes
% BYTES_PER_PCM_BLOCK
;
117 guint
track_block_get_used_bytes(const struct track_t
* tr
, const guint index
)
119 guint count
= track_get_used_blocks(tr
);
121 if(count
< (index
+ 1))
123 else if(count
== (index
+ 1))
124 return tr
->bytes
- (index
* BYTES_PER_PCM_BLOCK
);
126 return BYTES_PER_PCM_BLOCK
;
129 void track_blocks_shrink(struct track_t
* tr
, const guint newSize
)
131 //new size contains an invalid value
132 if(newSize
>= tr
->blocks
)
135 for(guint index
= tr
->blocks
- 1; index
> newSize
- 1; index
--)
137 g_slice_free(struct track_block_t
, track_get_block(tr
, index
));
140 g_ptr_array_remove_range(tr
->block
, newSize
, tr
->blocks
- newSize
);
144 "Deallocated track blocks (freed %d using %zu bytes).\n",
145 tr
->blocks
- newSize
,
146 (tr
->blocks
- newSize
) * sizeof(struct track_block_t
));
148 tr
->blocks
= newSize
;
151 /* Conclude the importer process. To be called whether the importer
152 * was aborted or completed successfully */
154 static int stop_import(struct track_t
*tr
)
158 if(close(tr
->fd
) == -1) {
165 cache_read_destroy(tr
->cache_read
);
166 tr
->cache_read
= NULL
;
167 cached_track_destroy(tr
->cache
);
174 if(waitpid(tr
->pid
, &status
, 0) == -1) {
179 fprintf(stderr
, "Track importer exited with status %d.\n", status
);
184 guint
track_get_sample_count(const struct track_t
* tr
)
186 return tr
->bytes
/ BYTES_PER_SAMPLE
;
189 /* Read the next block of data from the file. Return -1 when an error
190 * occurs and requires our attention, 1 if there is no more data to be
191 * read, otherwise zero. */
193 static int read_from_pipe(struct track_t
*tr
)
199 struct track_block_t
*block
;
201 /* Check whether we need to allocate a new block */
203 if(tr
->bytes
>= tr
->blocks
* BYTES_PER_PCM_BLOCK
) {
204 block
= g_slice_new(struct track_block_t
);
212 g_ptr_array_add(tr
->block
, block
);
214 fprintf(stderr
, "Allocated new track block (%d at %zu bytes).\n",
215 tr
->blocks
, tr
->bytes
);
218 /* Load in audio to the end of the current block. We've just
219 * allocated a new one if needed, so no possibility of read()
220 * returning zero, except for EOF */
222 block
= track_get_block(tr
, tr
->bytes
/ BYTES_PER_PCM_BLOCK
);
225 r
= cache_read_block(tr
->cache_read
, block
->pcm
, TRACK_BLOCK_PCM_LENGTH
);
228 guint used
= track_get_last_block_used_bytes(tr
);
230 r
= read(tr
->fd
, (char*)block
->pcm
+ used
,
231 BYTES_PER_PCM_BLOCK
- used
);
241 m
= BYTES_PER_PCM_BLOCK
* tr
->blocks
/ 1024;
242 fprintf(stderr
, "Track memory %zuKb PCM, %zuKb PPM, %zuKb overview.\n",
243 m
, m
/ TRACK_PPM_RES
, m
/ TRACK_OVERVIEW_RES
);
248 /* Meter the audio which has just been read */
250 for(s
= track_get_sample_count(tr
); s
< (tr
->bytes
+ r
) / BYTES_PER_SAMPLE
; s
++) {
251 ls
= s
% TRACK_BLOCK_SAMPLES
;
253 v
= (abs(block
->pcm
[ls
* TRACK_CHANNELS
])
254 + abs(block
->pcm
[ls
* TRACK_CHANNELS
]));
256 /* PPM-style fast meter approximation */
259 tr
->ppm
+= (v
- tr
->ppm
) >> 3;
261 tr
->ppm
-= (tr
->ppm
- v
) >> 9;
263 block
->ppm
[ls
/ TRACK_PPM_RES
] = tr
->ppm
>> 8;
265 /* Update the slow-metering overview. Fixed point arithmetic
271 tr
->overview
+= (w
- tr
->overview
) >> 8;
273 tr
->overview
-= (tr
->overview
- w
) >> 17;
275 block
->overview
[ls
/ TRACK_OVERVIEW_RES
] = tr
->overview
>> 24;
284 void track_init(struct track_t
*tr
, const char *importer
)
286 tr
->importer
= importer
;
293 tr
->cache_read
= NULL
;
296 tr
->block
= g_ptr_array_new();
300 pthread_mutex_init(&tr
->mx
, 0); /* always returns zero */
302 tr
->status
= TRACK_STATUS_VALID
;
305 /* Destroy this track from memory, and any child process */
307 int track_clear(struct track_t
*tr
)
313 /* Force a cleanup of whichever state we are in */
315 if(tr
->status
> TRACK_STATUS_VALID
) {
316 if(tr
->status
== TRACK_STATUS_IMPORTING
) {
317 if(!tr
->cache_read
&& kill(tr
->pid
, SIGKILL
) == -1) {
322 if(stop_import(tr
) == -1)
326 track_blocks_shrink(tr
, 0);
328 g_ptr_array_free(tr
->block
, FALSE
);
330 if(pthread_mutex_destroy(&tr
->mx
) == EBUSY
) {
331 fprintf(stderr
, "Track busy on destroy.\n");
335 cached_track_destroy(tr
->cache
);
342 /* Return the number of file descriptors which should be watched for
343 * this track, and fill pe */
345 int track_pollfd(struct track_t
*tr
, struct pollfd
*pe
)
351 if(tr
->status
== TRACK_STATUS_IMPORTING
&& !tr
->eof
) {
354 pe
->events
= POLLIN
| POLLHUP
| POLLERR
;
367 /* Handle any activity on this track, whatever the current state */
369 int track_handle(struct track_t
*tr
)
373 /* Only one thread is allowed to call this function, and it owns
376 if(!tr
->pe
|| !tr
->pe
->revents
)
381 if(tr
->status
== TRACK_STATUS_IMPORTING
&& !tr
->eof
) {
382 r
= read_from_pipe(tr
);
386 cached_track_write(tr
->cache
, tr
);
388 guint currentUsedBlocks
= track_get_used_blocks(tr
);
390 //currently we have more blocks allocated
392 if(currentUsedBlocks
< tr
->blocks
)
394 guint averageUsedBlocks
= get_average_blocks(track_get_used_blocks(tr
));
395 track_blocks_shrink(tr
, MAX(averageUsedBlocks
, currentUsedBlocks
));
407 /* A request to begin importing a new track. Can be called when the
408 * track is in any state */
410 int track_import(struct track_t
*tr
, const char *path
)
416 /* Abort any running import process */
418 if(tr
->status
!= TRACK_STATUS_VALID
) {
419 if(tr
->status
== TRACK_STATUS_IMPORTING
) {
420 if(!tr
->cache_read
&& kill(tr
->pid
, SIGTERM
) == -1) {
426 if(stop_import(tr
) == -1) {
432 /* Start the new import process */
434 r
= start_import(tr
, path
);
439 tr
->status
= TRACK_STATUS_IMPORTING
;
449 /* Do any waiting for completion of importer process if needed */
451 int track_wait(struct track_t
*tr
)
455 if(tr
->status
== TRACK_STATUS_IMPORTING
&& tr
->eof
) {
457 tr
->status
= TRACK_STATUS_VALID
;