/** \file afh.c Paraslash's standalone audio format handler tool. */
#include <dirent.h>
+#include <sys/time.h>
#include "para.h"
#include "string.h"
INIT_AFH_ERRLISTS;
INIT_STDERR_LOGGING(conf.loglevel_arg)
+static void print_info(int audio_format_num, struct afh_info *afhi)
+{
+ printf("%s: %dkbit/s\n" /* bitrate */
+ "%s: %s\n" /* format */
+ "%s: %dHz\n" /* frequency */
+ "%s: %d\n" /* channels */
+ "%s: %lu\n" /* seconds total */
+ "%s" /* tag info */
+ "%s: %lu: %lu\n" /* chunk time */
+ "%s: %lu\n", /* num chunks */
+ status_item_list[SI_BITRATE], afhi->bitrate,
+ status_item_list[SI_FORMAT], audio_format_name(audio_format_num),
+ status_item_list[SI_FREQUENCY], afhi->frequency,
+ status_item_list[SI_CHANNELS], afhi->channels,
+ status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total,
+ afhi->info_string,
+ status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec,
+ (long unsigned)afhi->chunk_tv.tv_usec,
+ status_item_list[SI_NUM_CHUNKS], afhi->chunks_total
+ );
+}
+
+static void print_chunk_table(struct afh_info *afhi)
+{
+ int i;
+
+ printf("chunk_table: ");
+ for (i = 0; i <= afhi->chunks_total; i++)
+ printf("%u ", afhi->chunk_table[i]);
+ printf("\n");
+}
+
+static int cat_file(void *audio_file_data, struct afh_info *afhi)
+{
+ int ret;
+ struct timeval stream_start;
+ long unsigned i, first_chunk, last_chunk;
+ char *buf;
+ size_t size;
+
+
+ if (conf.begin_chunk_arg < 0)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ first_chunk = conf.begin_chunk_arg;
+
+ if (conf.end_chunk_given) {
+ if (conf.end_chunk_arg < 0)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (conf.end_chunk_arg >= afhi->chunks_total)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ last_chunk = conf.end_chunk_arg;
+ } else
+ last_chunk = afhi->chunks_total - 1;
+ if (!afhi->chunks_total)
+ return 1;
+ afh_get_header(afhi, audio_file_data, &buf, &size);
+ if (size && first_chunk && !conf.no_header_given) {
+ PARA_INFO_LOG("writing audio file header (%zu bytes)\n", size);
+ ret = write(STDOUT_FILENO, buf, size);
+ if (ret < 0)
+ return ret;
+ if (ret != size)
+ return -E_AFH_SHORT_WRITE;
+ }
+ PARA_NOTICE_LOG("writing chunks %lu - %lu\n", first_chunk, last_chunk);
+ gettimeofday(&stream_start, NULL);
+ for (i = first_chunk; i <= last_chunk; i++) {
+ struct timeval now, diff, next_chunk;
+ afh_get_chunk(i, afhi, audio_file_data, &buf, &size);
+ PARA_DEBUG_LOG("chunk %lu: size %zu\n", i, size);
+ if (conf.just_in_time_given) {
+ compute_chunk_time(i - first_chunk, &afhi->chunk_tv,
+ &stream_start, &next_chunk);
+ gettimeofday(&now, NULL);
+ ret = tv_diff(&next_chunk, &now, &diff);
+ if (ret > 0) {
+ ret = para_select(1, NULL, NULL, &diff);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ PARA_INFO_LOG("writing chunk %lu\n", i);
+ ret = write(STDOUT_FILENO, buf, size);
+ if (ret < 0)
+ return ret;
+ if (ret != size)
+ return -E_AFH_SHORT_WRITE;
+ }
+ return 1;
+}
+
int main(int argc, char **argv)
{
- int i, ret;
+ int ret, audio_format_num;
void *audio_file_data;
size_t audio_file_size;
struct afh_info afhi;
ret = compute_afhi(conf.inputs[0], audio_file_data, audio_file_size, &afhi);
if (ret < 0)
goto out;
- printf("%s: %dkbit/s\n" /* bitrate */
- "%s: %s\n" /* format */
- "%s: %dHz\n" /* frequency */
- "%s: %d\n" /* channels */
- "%s: %lu\n" /* seconds total */
- "%s" /* tag info */
- "%s: %lu: %lu\n" /* chunk time */
- "%s: %lu\n", /* num chunks */
- status_item_list[SI_BITRATE], afhi.bitrate,
- status_item_list[SI_FORMAT], audio_format_name(ret),
- status_item_list[SI_FREQUENCY], afhi.frequency,
- status_item_list[SI_CHANNELS], afhi.channels,
- status_item_list[SI_SECONDS_TOTAL], afhi.seconds_total,
- afhi.info_string,
- status_item_list[SI_CHUNK_TIME], (long unsigned)afhi.chunk_tv.tv_sec,
- (long unsigned)afhi.chunk_tv.tv_usec,
- status_item_list[SI_NUM_CHUNKS], afhi.chunks_total
- );
- ret = 1;
- if (!conf.chunk_table_given)
- goto out;
- printf("chunk_table: ");
- for (i = 0; i <= afhi.chunks_total; i++)
- printf("%u ", afhi.chunk_table[i]);
- printf("\n");
- ret = 1;
+ audio_format_num = ret;
+ if (conf.stream_given)
+ ret = cat_file(audio_file_data, &afhi);
+ else {
+ print_info(audio_format_num, &afhi);
+ if (conf.chunk_table_given)
+ print_chunk_table(&afhi);
+ ret = 1;
+ }
out:
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+text "
+para_afh, the audio format handler tool, is a stand-alone program
+contained in the paraslash package for analyzing and streaming audio
+files. It can be used to
+
+ - print tech info about the given audio file to stdout.
+ In particular, the 'chunk table' of the audio file, an array
+ of offsets within the audio file, may be printed. This table
+ can be used by other programs unaware of the particular audio
+ format to stream the audio file.
+
+ - write selected parts of the given audio file in complete
+ chunks without decoding. Thus para_afh can be used to 'cut'
+ an audio file.
+
+ - write selected parts of the given audio files 'just in time'
+ to sdout. This may be useful for third-party software that
+ is capable of reading from stdin.
+"
+
option "loglevel" l
#~~~~~~~~~~~~~~~~~~
"set loglevel (0-6)"
+int typestr="level"
+default="4"
+optional
+
+defgroup "mode"
+#--------------
+groupdesc="
+ There are two modes of operation: Info mode and stream mode,
+ one of which must be selected by the corresponding option.
+ See below.
+"
+required
+
+groupoption "info" i
+#~~~~~~~~~~~~~~~~~~~
+"select info mode"
+group="mode"
+details="
+ In this mode, the program prints technical information about
+ the given audio file to stdout.
+"
- int typestr="level"
- default="4"
- optional
+groupoption "stream" s
+#~~~~~~~~~~~~~~~~~~~~~
+"select stream mode"
+group="mode"
+details="
+ If this mode is selected, the selected parts of the content
+ of the audio file are written to stdout. Only complete chunks
+ with respect of the underlying audio format are written.
+ For example, only complete frames in case of mp3 files.
+"
+
+section "Options for info mode"
+#==============================
option "chunk_table" c
+#~~~~~~~~~~~~~~~~~~~~~
"print also the chunk table"
+flag off
+dependon="info"
+
+section "Options for stream mode"
+#================================
+
+
+option "begin_chunk" b
+#~~~~~~~~~~~~~~~~~~~~~
+"skip a number of chunks"
+int typestr="chunk_num"
+default="0"
+dependon="stream"
+optional
+details="
+ The chunk_num argument must be non-negative as chunks start
+ at zero.
+"
+
+option "end_chunk" e
+#~~~~~~~~~~~~~~~~~~~
+"only write up to chunk chunk_num"
+int typestr="chunk_num"
+dependon="stream"
+optional
+details="
+ The chunk_num argument must be less than the total number of
+ chunks. The default is to write up to the last chunk.
+"
+
+option "just_in_time" j
+#~~~~~~~~~~~~~~~~~~~~~~
+"use timed writes"
+flag off
+dependon="stream"
+details="
+ Write the specified chunks of data 'just in time', i.e. the
+ write of each chunk is delayed until the time it is needed
+ by the decoder/player in order to guarantee an uninterupted
+ audio stream.
+"
- flag off
+option "no_header" H
+#~~~~~~~~~~~~~~~~~~~
+"do not write an audio file header"
+flag off
+dependon="stream"
+details="
+ If an audio format needs information about the audio file
+ in a format-specific header in order to be understood by
+ the decoding software, a suitable header is automatically
+ send. This option changes the default behaviour so that no
+ header is sent.
+"