diff options
Diffstat (limited to 'mpeglib/lib/tplay')
-rw-r--r-- | mpeglib/lib/tplay/CHANGES | 154 | ||||
-rw-r--r-- | mpeglib/lib/tplay/Makefile.am | 15 | ||||
-rw-r--r-- | mpeglib/lib/tplay/README | 95 | ||||
-rw-r--r-- | mpeglib/lib/tplay/au.cpp | 100 | ||||
-rw-r--r-- | mpeglib/lib/tplay/tplayfunctions.cpp | 84 | ||||
-rw-r--r-- | mpeglib/lib/tplay/tplayfunctions.h | 128 | ||||
-rw-r--r-- | mpeglib/lib/tplay/wav.cpp | 91 |
7 files changed, 667 insertions, 0 deletions
diff --git a/mpeglib/lib/tplay/CHANGES b/mpeglib/lib/tplay/CHANGES new file mode 100644 index 00000000..aad2446d --- /dev/null +++ b/mpeglib/lib/tplay/CHANGES @@ -0,0 +1,154 @@ +Version 0.1, 2.4.1997: + + - the first released version + +5.4.1997: + + - audio sync added before changing parameters + +Version 0.2, 9.4.1997: + + - it was useless to start producer as a thread; + it was made that way just for historical reasons. + only consumer is threaded now. this may result + as more robust behaviour. + + - there are min and max sizes for block now. i'm + not sure yet what size for a block and the audio + buffer would be good. needs more research. + + - fill_buffer function. fills the audio buffer + before use. + +Version 0.2.1, 15.4.1997: + + - signal() seems to be a bad idea in a threaded + application like this. causes kernel oops in + the sound driver function audio_write (sometimes). + let's have faith on the kernel and remove it. + +Version 0.2.2, 17.4.1997 morning: + + - the last block was written from very wrong point. + + - block counting added. this makes stream ending + simpler and (hopefully) more robust. + + - first lines for handling underflow (buffer empty) + situation. + +Version 0.3, 17.4.1997 afternoon: + + - underflow handling should work now. + + - function buffer_usage added. returns buffer usage + in percents. nowhere used yet. + + - minimum block size increased to 16k. + +Version 0.3.1, 19.4.1997: + + - GNU style options. + + - option -v (or --version) added. + + - buffer usage option -u (or --usage) added. + +Version 0.4, 2.5.1997: + + - support for RIFF/WAVE (WAV) and Sun audio + (AU) files. + + - swap endianness flag -x (or --swap) added. + + - verbose mode flag -V (or --verbose) added. + + - force raw flag -r (or --raw) added. WAV- or + AU-file headers are ignored if this flag is set. + +Version 0.4.1, 3.5.1997: + + - sun header gives odd aligned starting point + for sample. temporary fix. + + - read_big_endian_long returned wrong value if + sampling rate was 44100. this caused tplay + not to work with that speed when playing sun + audio or wav file. fixed now. + +Version 0.4.2, 7.5.1997: + + - read_big_endian_long and similar functions: + parameter's type was char* and that was a bug. + changed to byte* (unsigned char *). conversion + should also be saner now. + + - force playing -f (or --force) flag added. this + makes tplay to ignore sound driver's results + when changing parameters. + +Version 0.4.3, 12.5.1997: + + - binary is statically linked to LinuxThreads + version 0.6 now. + + - print sun header comment if verbose is requested. + + - sun audio file's data stream starting pointer + is read and set from the header. + + - to avoid rounding errors when playing 16bit + and/or stereo (au or wav) sample, data section + is moved now to the beginning of buffer before + playing. + +Version 0.5, 23.10.1997: + + - set_audio_parameters() partly rewritten and + changes to open_audio(). + + - in some WAV-files, data-portion is not started + with 'data'-magic but 'INFO' instead. some + players don't even check that so tplay prints + just a warning message now if neither of these + magics exists. + +Version 0.5.1, 25.10.1997: + + - printing of buffer usage changed from producer + to consumer. this makes this silly feature a bit + more informative as it is still active after the + producer has stopped. underflow situation (when + the big buffer needs to be refilled) is also + possible to show now. + +Version 0.5.2, 9.5.1998: + + - Jerko Golubovic <jerko.golubovic@public.srce.hr> + kindly modified the code to support those soundcards + that may result slightly different sampling rate + than requested. he also provided RPM of tplay. + + - added -D (or --device=DEVICE) flag for setting + audio device to be used. + + - added feature to -B (or --buffer-size=SIZE) flag. + buffer size can be given in seconds now, too. + +Version 0.5.3, 11.5.1998: + + - rewrite of playing routine to support multiple + sound samples from command line. + +Version 0.5.4, 19.5.1998: + + - added -l (or --loop) flag to support looping sound + samples. + + - sun port. + +Version 0.5.5, 24.5.1998: + + - added environment variable TPLAYDEV, which sets the + audio device to be used. decoding of command line + options is moved from main() to another function. diff --git a/mpeglib/lib/tplay/Makefile.am b/mpeglib/lib/tplay/Makefile.am new file mode 100644 index 00000000..c83b25ea --- /dev/null +++ b/mpeglib/lib/tplay/Makefile.am @@ -0,0 +1,15 @@ +# libtplay - Makefile.am + + +EXTRA_DIST = CHANGES README + +INCLUDES = $(all_includes) + + +noinst_LTLIBRARIES = libtplay.la + +noinst_HEADERS = tplayfunctions.h + +libtplay_la_SOURCES = au.cpp \ + tplayfunctions.cpp wav.cpp + diff --git a/mpeglib/lib/tplay/README b/mpeglib/lib/tplay/README new file mode 100644 index 00000000..275d6a9f --- /dev/null +++ b/mpeglib/lib/tplay/README @@ -0,0 +1,95 @@ +This is a buffered audio player for Linux. POSIX-thread library is +used. This is still considered BETA software and may not work as +expected. Please mail me for bug reports, opinions or suggestions. + +This is primarily made for use with MPEG-decoders. They typically +consume lots of CPU-time and some kind of audio buffer is needed to +reduce cutting while writing to audio device. You can also play any +audio files with tplay or use it with any program that writes audio +data to standard out. + +RIFF/WAVE (WAV) and Sun audio (AU) file headers are recognized by +now. + +Binary: + +In the source tree there is a readily compiled binary that is build +under Linux/ELF 2.0.30 with libc 5.4.20 and LinuxThreads 0.6 (the +thread library is statically linked). + +Command line options: + + tplay [-hvVmuxrf] [-s Hz] [-b 8|16] [-B kilobytes] [filename] + + -h, --help Print help, then exit + -v, --version Print version, then exit + -V, --verbose Print useful information about the sample + -x, --swap Swap endianness + -r, --raw Force raw audio format. Ignore headers. + -f, --force Force playing with any parameters + -m, --mono Mono sample + -u, --usage Print buffer usage while playing + -s, --speed=SPEED Sample speed (Hz) + -b, --bytes=BYTES Bytes in a sample + -B, --buffer-size=SIZE Buffer size in (kB) + +Buffer size is defaulted to 512k. It is about 3 seconds CD audio +(44100Hz/sample, 16bytes, stereo). If filename is not given, standard +input is used. If -x (or --swap) flag is set, the byte order of +audio sample is swapped before playing. The default is Intel little- +endian which is mostly used in x86 machines. The world outside Intel +is big-endian. +Option -r (or --raw) forces tplay to handle the sample as an raw +PCM audio sample. Sun audio or WAV headers are ignored. + +Requirements: + + - Linux 2.0 or newer with audio card support + - POSIX thread library + - Audio card + +There are several POSIX thread libraries available. I used +LinuxThreads by Xavier Leroy (Xavier.Leroy@inria.fr). LinuxThreads +library use clone() that is provided by Linux 2.0 kernel. + +The code: + +tplay starts one thread, named consumer, that reads circular audio +buffer and writes it to audio device. The producer is a function that +runs in parallel with the consumer and its task is to read the sample +file or standard input and write this data to audio buffer to meet +consumer's needs. Usually, the buffer is full but on the times when +CPU-time is suddenly needed for other processes (usually: disk +read/write), the producer can't write fast enough and consumer can use +the buffer to keep audio stream uninterrupted. If the buffer is used +and the producer is still unable to feed it fast enough, underflow +situation is met and consumer waits for awhile (typically: one second) +for the producer to fill the buffer again. + +Building: + +If you want to link tplay with static libpthread library, edit +Makefile and uncomment preferred LIBS-setting there. Type: + + - make + - make install + - make install.man + +Thanks: + +Jerko Golubovic <jerko.golubovic@public.srce.hr> +Jukka Palviainen <oh3kjt@ele.tut.fi> + +TODO: + +Find out the best sizes for the audio buffer and one block. +Better documentation. +Better RIFF/WAVE checking. + +Ilkka Karvinen +ik@iki.fi + + + + + diff --git a/mpeglib/lib/tplay/au.cpp b/mpeglib/lib/tplay/au.cpp new file mode 100644 index 00000000..8880515f --- /dev/null +++ b/mpeglib/lib/tplay/au.cpp @@ -0,0 +1,100 @@ +/* + * tplay - buffered audio player + * + * (c) 1997 ilkka karvinen <ik@iki.fi> + * + * Copyright under the GNU GENERAL PUBLIC LICENSE + * (see the file COPYING in this directory) + * + * + * SunOS audio file header functions. + * Reference: http://www.wotsit.org + */ + +#include "tplayfunctions.h" + +/* read_au returns zero if Sun audio file format is found. */ +int read_au(struct info_struct* info,char * buffer) { + DWORD magic, start, end, encoding, speed, channels; + int bits; + + + /* If '.snd'-header exits, this should be an au-file */ + magic = read_big_endian_long(buffer); + if (magic != SUN_MAGIC) + return (1); + + start = read_big_endian_long(buffer + 0x04); + end = read_big_endian_long(buffer + 0x08); + encoding = read_big_endian_long(buffer + 0x0C); + speed = read_big_endian_long(buffer + 0x10); + channels = read_big_endian_long(buffer + 0x14); + +#ifdef DEBUG + printf("Sun audio file.\nspeed: %ld, start: %ld, end: %ld, \ +encoding: %X, channels: %ld\n", + speed, start, end, encoding, channels); + fflush(stdout); +#endif + + bits = DEFAULT_BITS; + switch (encoding) { + case 1: + die("8-bit ISDN u-law Sun audio file not supported"); + break; + case 2: + bits = 8; + break; + case 3: + bits = 16; + break; + case 4: + die("24-bit linear PCM Sun audio file not supported"); + break; + case 5: + die("32-bit linear PCM Sun audio file not supported"); + break; + case 6: + die("32-bit IEEE floating point Sun audio file not supported"); + break; + case 7: + die("64-bit IEEE floating point Sun audio file not supported"); + break; + case 23: + die("8-bit ISDN u-law compressed(G.721 ADPCM) Sun audio file \ +not supported"); + break; + default: + errdie("Unknown Sun audio file"); + break; + } + + info->filetype = SUN_FILE; + + /* Set audio parameters */ + info->speed = (int) speed; + info->bits = bits; + info->channels = (int) channels; + + if (info->verbose) { + printf("Sun audio file: %ld samples/s, %d bits, %d channel(s).\n", + info->speed, info->bits, info->channels); + /* + if ((comment_size = start - SUN_HDRSIZE) > 0) { + printf("Header info: "); + for (i = 0; i < comment_size; i++) + nice_fputc((int) buffer[SUN_HDRSIZE + i], stdout); + printf("\n"); + } + */ + } + + /* Move data to start from the beginning of the buffer. */ + /* This is to ensure the correct behaviour of rounding when 16bits */ + /* and/or stereo sample is to be played. */ + memmove(buffer, buffer + (start + 1), info->blocksize - start - 1); + + info->headerskip = (int) (start + 1); + + return (0); +} diff --git a/mpeglib/lib/tplay/tplayfunctions.cpp b/mpeglib/lib/tplay/tplayfunctions.cpp new file mode 100644 index 00000000..c105f4a2 --- /dev/null +++ b/mpeglib/lib/tplay/tplayfunctions.cpp @@ -0,0 +1,84 @@ +/* + * tplay - buffered audio player + * + * (c) 1997 ilkka karvinen <ik@iki.fi> + * + * Copyright under the GNU GENERAL PUBLIC LICENSE + * (see the file COPYING in this directory) + * + * + * common functions + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "tplayfunctions.h" + +DWORD read_big_endian_long(char * buf) +{ + DWORD byte0, byte1, byte2, byte3; + unsigned char* buffer=(unsigned char*) buf; + + byte0 = (DWORD) buffer[0]; + byte1 = (DWORD) buffer[1]; + byte2 = (DWORD) buffer[2]; + byte3 = (DWORD) buffer[3]; + return (byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3); +} + +void write_big_endian_long(char * buf, DWORD value) +{ + unsigned char* buffer=(unsigned char*) buf; + buffer[0] = (unsigned char) (value >> 24 & 0xFF); + buffer[1] = (unsigned char) (value >> 16 & 0xFF); + buffer[2] = (unsigned char) (value >> 8 & 0xFF); + buffer[3] = (unsigned char) (value & 0xFF); +} + +DWORD read_little_endian_long(char* buf) { + DWORD byte0, byte1, byte2, byte3; + unsigned char* buffer=(unsigned char*) buf; + + byte0 = (DWORD) buffer[0]; + byte1 = (DWORD) buffer[1]; + byte2 = (DWORD) buffer[2]; + byte3 = (DWORD) buffer[3]; + return (byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0); +} + +WORD read_little_endian_word(char * buf) +{ + WORD byte0, byte1; + unsigned char* buffer=(unsigned char*) buf; + + byte0 = (WORD) buffer[0]; + byte1 = (WORD) buffer[1]; + return (byte1 << 8 | byte0); +} + +void errprintf(char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); +} + + + +void die(const char *str) +{ + fprintf(stderr, "%s: \n", str); + exit(-1); +} + +void errdie(const char *str) +{ + fprintf(stderr, "Error: %s\n", str); + exit(-1); +} + + + diff --git a/mpeglib/lib/tplay/tplayfunctions.h b/mpeglib/lib/tplay/tplayfunctions.h new file mode 100644 index 00000000..0c159847 --- /dev/null +++ b/mpeglib/lib/tplay/tplayfunctions.h @@ -0,0 +1,128 @@ +/* + * tplay - buffered audio player header file + * + * (c) 1997 ilkka karvinen <ik@iki.fi> + * + * Copyright under the GNU GENERAL PUBLIC LICENSE + * (see the file COPYING in this directory) + * + */ + +#ifndef __TPLAYCONTROL_H +#define __TPLAYCONTROL_H + +extern "C" { +#include <stdio.h> +#include <string.h> +} + +/* tplay version */ +#define MAJOR_VERSION 0 +#define MINOR_VERSION 5 +#define PATCHLEVEL 5 + +/* Default audio parameters */ +#define DEFAULT_BITS 16 +#define DEFAULT_SPEED 44100 +#define DEFAULT_CHANNELS 2 + +/* Audio memory pool. 512k is the default. */ +#define BUFFER_SIZE 0x80000 + +/* The minimum and maximum buffer block sizes. */ +#if 0 +#define MIN_BLOCK_SIZE 0x4000 /* 16k */ +#else +#define MIN_BLOCK_SIZE 4096 +#endif +#define MAX_BLOCK_SIZE 0x10000 /* 64k */ + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* The maximum retry count for buffer fill tries. */ +#define RETRY_COUNT 5 + +/* Magics. Little-endian. */ +#define RIFF_MAGIC 0x46464952 /* ASCII: 'RIFF' */ +#define WAVE_MAGIC 0x45564157 /* ASCII: 'WAVE' */ +#define DATA_MAGIC 0x61746164 /* ASCII: 'data' */ +#define INFO_MAGIC 0x4f464e49 /* ASCII: 'INFO' */ +#define SUN_MAGIC 0x2e736e64 /* ASCII: '.snd' */ + +/* Magics. Big-endian. */ +#define SUN_INV_MAGIC 0x646e732e /* ASCII: '.snd' */ + +/* Sun headersize */ +#define SUN_HDRSIZE 24 + +/* File types */ +#define UNKNOWN_FILE 0 +#define RIFF_FILE 1 +#define SUN_FILE 2 + +typedef unsigned long DWORD; +typedef unsigned short WORD; + +/* Circular buffer info structure of audio data blocks. */ +/* declared in tplay.c */ +struct info_struct { + char *buffer; /* the audio data */ + char *firstblock; /* pointer to the first block */ + int readblock, writeblock; /* reading and writing block number */ + long readcount, writecount; + int alldone; + int in_seconds; + double seconds; + int blocksize; /* size of one block */ + int buffer_size; /* size of the buffer */ + int number_of_blocks; + int last_block; /* -1 if not the last block */ + int bytes_on_last_block; + int overflow; + int underflow; + int swap; + int forceraw; + int force; + int filetype; + int headerskip; + int audioset; + int show_usage; + DWORD speed; + int channels; + int bits; + char *progname; + char *device; /* Audio device name */ + int loop; + int verbose; + int optind; +}; + +/* au.c */ + int read_au(struct info_struct* info, char * buffer); + +/* wav.c */ + int read_wav(struct info_struct* info, char * buffer); + +/* common.c */ + DWORD read_big_endian_long(char * buffer); + void write_big_endian_long(char * buffer, DWORD value); + DWORD read_little_endian_long(char * buffer); + WORD read_little_endian_word(char * buffer); + void errprintf(char *fmt,...); + void warning(char *str); + void warning2(char *str1, char *str2); + void die(const char *str); + void errdie(const char *str); + void open_audio(); + void set_audio_parameters(); + void sync_audio(void); + void reset_audio(void); + void post_audio(void); + void destroy_buffer(void); + void nice_fputc(int c, FILE * fp); + + +#endif diff --git a/mpeglib/lib/tplay/wav.cpp b/mpeglib/lib/tplay/wav.cpp new file mode 100644 index 00000000..ca284c37 --- /dev/null +++ b/mpeglib/lib/tplay/wav.cpp @@ -0,0 +1,91 @@ +/* + * tplay - buffered audio player + * + * (c) 1997 ilkka karvinen <ik@iki.fi> + * + * Copyright under the GNU GENERAL PUBLIC LICENSE + * (see the file COPYING in this directory) + * + * + * RIFF/WAVE file header checking. + * check_wav returns zero if RIFF/WAVE file format is found. + * Reference: http://www.wotsit.demon.co.uk/formats/wav/wav.txt + */ + +#include "tplayfunctions.h" + +#include <iostream> + +using namespace std; + +int read_wav(struct info_struct* info, char * buffer) { + WORD format, channels, bits; + DWORD magic, samples_per_second, length, data_length; + + magic = read_little_endian_long(buffer); + if (magic != RIFF_MAGIC) /* RIFF file? */ + return (1); + magic = read_little_endian_long(buffer + 0x08); + if (magic != WAVE_MAGIC) /* WAVE file? */ + return (1); + magic = read_little_endian_long(buffer + 0x24); + if ((magic != DATA_MAGIC) && (magic != INFO_MAGIC)) { /* data-portion there? */ + cout << "Unknown WAV-header magic. Continuing anyway."<<endl; + } + + length = read_little_endian_long(buffer + 0x10); + + /* Subchunk length should be 16 here */ + if (length != 16) + errdie("Unknown RIFF/WAVE header"); + + format = read_little_endian_word(buffer + 0x14); + + switch (format) { + case 0x0001: /* PCM format */ + break; + case 0x0101: /* mu-law */ + die("Mu-law RIFF/WAVE audio file not supported"); + break; + case 0x0102: /* a-law */ + die("A-law RIFF/WAVE audio file not supported"); + break; + case 0x0103: /* ADPCM */ + die("ADPCM RIFF/WAVE audio file not supported"); + break; + default: + errdie("Unknown RIFF/WAVE audio file format"); + break; + } + + info->filetype = RIFF_FILE; + + channels = read_little_endian_word(buffer + 0x16); + samples_per_second = read_little_endian_long(buffer + 0x18); + cout << "samples_per_second:"<<samples_per_second<<endl; + bits = read_little_endian_word(buffer + 0x22); + + if (bits == 12) + die("12 bits per sample not supported"); + + data_length = read_little_endian_long(buffer + 0x28); + + /* Set audio parameters */ + info->speed = (int) samples_per_second; + info->bits = (int) bits; + info->channels = (int) channels; + + if (info->verbose) + printf("RIFF/WAVE audio file: %ld samples/s, %d bits, %d channel(s).\n", + info->speed, info->bits, info->channels); + + /* Move data to start from the beginning of the buffer. */ + /* This is to ensure the correct behaviour of rounding when 16bits */ + /* and/or stereo sample is to be played. */ + memmove(buffer, buffer + 0x2c, info->blocksize - 0x2c); + + /* Save audio sample starting point */ + info->headerskip = 0x2c; + + return (0); +} |