diff --git a/Makefile b/Makefile index 38f7eb85..6382c1fb 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ mingw_winamp: $(MAKE) -C winamp in_vgmstream.dll clean: - rm -f vgmstream-* + rm -rf vgmstream-* $(MAKE) -C test clean $(MAKE) -C test -f Makefile.mingw clean $(MAKE) -C winamp clean diff --git a/configure.in b/configure.in index 5afcb684..1bab0b99 100644 --- a/configure.in +++ b/configure.in @@ -23,6 +23,10 @@ PKG_CHECK_MODULES(VORBISFILE, [vorbisfile],, [AC_MSG_ERROR([Cannot find libvorbisfile])] ) +PKG_CHECK_MODULES(MPG123, [libmpg123],, + [AC_MSG_ERROR([Cannot find libmpg123])] +) + dnl Check for GTK/GLib/GThread/Pango PKG_CHECK_MODULES(GTK, [glib-2.0 >= 2.6.0 gtk+-2.0 >= 2.6.0 gthread-2.0 pango], @@ -30,7 +34,7 @@ PKG_CHECK_MODULES(GTK, [glib-2.0 >= 2.6.0 gtk+-2.0 >= 2.6.0 gthread-2.0 pango], ) CFLAGS="$CFLAGS $AUDACIOUS_CFLAGS" -LIBS="$LIBS $AUDACIOUS_LIBS $GTK_LIBS $VORBISFILE_LIBS" +LIBS="$LIBS $AUDACIOUS_LIBS $GTK_LIBS $VORBISFILE_LIBS $MPG123_LIBS" plugindir=`pkg-config audacious --variable=plugin_dir` AC_SUBST(plugindir) diff --git a/ext_includes/mpg123.h b/ext_includes/mpg123.h new file mode 100644 index 00000000..ab0d2626 --- /dev/null +++ b/ext_includes/mpg123.h @@ -0,0 +1,736 @@ +/* + libmpg123: MPEG Audio Decoder library (version 1.4.3) + + copyright 1995-2007 by the mpg123 project - free software under the terms of the LGPL 2.1 + see COPYING and AUTHORS files in distribution or http://mpg123.org +*/ + +#ifndef MPG123_LIB_H +#define MPG123_LIB_H + +/** \file mpg123.h The header file for the libmpg123 MPEG Audio decoder */ + +/* These aren't actually in use... seems to work without using libtool. */ +#ifdef BUILD_MPG123_DLL +/* The dll exports. */ +#define EXPORT __declspec(dllexport) +#else +#ifdef LINK_MPG123_DLL +/* The exe imports. */ +#define EXPORT __declspec(dllimport) +#else +/* Nothing on normal/UNIX builds */ +#define EXPORT +#endif +#endif + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup mpg123_init mpg123 library and handle setup + * + * Functions to initialise and shutdown the mpg123 library and handles. + * The parameters of handles have workable defaults, you only have to tune them when you want to tune something;-) + * Tip: Use a RVA setting... + * + * @{ + */ + +/** Opaque structure for the libmpg123 decoder handle. */ +struct mpg123_handle_struct; + +/** Opaque structure for the libmpg123 decoder handle. + * Most functions take a pointer to a mpg123_handle as first argument and operate on its data in an object-oriented manner. + */ +typedef struct mpg123_handle_struct mpg123_handle; + +/** Function to initialise the mpg123 library. + * This function is not thread-safe. Call it exactly once before any other work with the library. + * + * \return MPG123_OK if successful, otherwise an error number. + */ +EXPORT int mpg123_init(void); + +/** Function to close down the mpg123 library. + * This function is not thread-safe. Call it exactly once after any work with the library. */ +EXPORT void mpg123_exit(void); + +/** Create a handle with optional choice of decoder (named by a string, see mpg123_decoders() or mpg123_supported_decoders()). + * and optional retrieval of an error code to feed to mpg123_plain_strerror(). + * Optional means: Any of or both the parameters may be NULL. + * + * \return Non-NULL pointer when successful. + */ +EXPORT mpg123_handle *mpg123_new(const char* decoder, int *error); + +/** Delete handle, mh is either a valid mpg123 handle or NULL. */ +EXPORT void mpg123_delete(mpg123_handle *mh); + +/** Enumeration of the parameters types that it is possible to set/get. */ +enum mpg123_parms +{ + MPG123_VERBOSE, /**< set verbosity value for enabling messages to stderr, >= 0 makes sense */ + MPG123_FLAGS, /**< set all flags, p.ex val = MPG123_GAPLESS|MPG123_MONO_MIX */ + MPG123_ADD_FLAGS, /**< add some flags */ + MPG123_FORCE_RATE, /**< when value > 0, force output rate to that value */ + MPG123_DOWN_SAMPLE, /**< 0=native rate, 1=half rate, 2=quarter rate */ + MPG123_RVA, /**< one of the RVA choices above */ + MPG123_DOWNSPEED, /**< play a frame N times */ + MPG123_UPSPEED, /**< play every Nth frame */ + MPG123_START_FRAME, /**< start with this frame (skip frames before that) */ + MPG123_DECODE_FRAMES, /**< decode only this number of frames */ + MPG123_ICY_INTERVAL, /**< stream contains ICY metadata with this interval */ + MPG123_OUTSCALE, /**< the scale for output samples (amplitude) */ + MPG123_TIMEOUT, /**< timeout for reading from a stream (not supported on win32) */ + MPG123_REMOVE_FLAGS, /**< remove some flags (inverse of MPG123_ADD_FLAGS) */ + MPG123_RESYNC_LIMIT /**< Try resync on frame parsing for that many bytes or until end of stream (<0). */ +}; + +/** Flag bits for MPG123_FLAGS, use the usual binary or to combine. */ +enum mpg123_param_flags +{ + MPG123_FORCE_MONO = 0x7 /**< 0111 Force some mono mode: This is a test bitmask for seeing if any mono forcing is active. */ + ,MPG123_MONO_LEFT = 0x1 /**< 0001 Force playback of left channel only. */ + ,MPG123_MONO_RIGHT = 0x2 /**< 0010 Force playback of right channel only. */ + ,MPG123_MONO_MIX = 0x4 /**< 0100 Force playback of mixed mono. */ + ,MPG123_FORCE_STEREO = 0x8 /**< 1000 Force stereo output. */ + ,MPG123_FORCE_8BIT = 0x10 /**< 00010000 Force 8bit formats. */ + ,MPG123_QUIET = 0x20 /**< 00100000 Suppress any printouts (overrules verbose). */ + ,MPG123_GAPLESS = 0x40 /**< 01000000 Enable gapless decoding (default on if libmpg123 has support). */ + ,MPG123_NO_RESYNC = 0x80 /**< 10000000 Disable resync stream after error. */ + ,MPG123_SEEKBUFFER = 0x100 /**< 000100000000 Enable small buffer on non-seekable streams to allow some peek-ahead (for better MPEG sync). */ +}; + +/** choices for MPG123_RVA */ +enum mpg123_param_rva +{ + MPG123_RVA_OFF = 0 /**< RVA disabled (default). */ + ,MPG123_RVA_MIX = 1 /**< Use mix/track/radio gain. */ + ,MPG123_RVA_ALBUM = 2 /**< Use album/audiophile gain */ + ,MPG123_RVA_MAX = MPG123_RVA_ALBUM /**< The maximum RVA code, may increase in future. */ +}; + +/* TODO: Assess the possibilities and troubles of changing parameters during playback. */ + +/** Set a specific parameter, for a specific mpg123_handle, using a parameter + * type key chosen from the mpg123_parms enumeration, to the specified value. */ +EXPORT int mpg123_param(mpg123_handle *mh, enum mpg123_parms type, long value, double fvalue); + +/** Get a specific parameter, for a specific mpg123_handle. + * See the mpg123_parms enumeration for a list of available parameters. */ +EXPORT int mpg123_getparam(mpg123_handle *mh, enum mpg123_parms type, long *val, double *fval); + +/* @} */ + + +/** \defgroup mpg123_error mpg123 error handling + * + * Functions to get text version of the error numbers and an enumeration + * of the error codes returned by libmpg123. + * + * Most functions operating on a mpg123_handle simply return MPG123_OK on success and MPG123_ERR on failure (setting the internal error variable of the handle to the specific error code). + * Decoding/seek functions may also return message codes MPG123_DONE, MPG123_NEW_FORMAT and MPG123_NEED_MORE. + * The positive range of return values is used for "useful" values when appropriate. + * + * @{ + */ + +/** Enumeration of the message and error codes and returned by libmpg123 functions. */ +enum mpg123_errors +{ + MPG123_DONE=-12, /**< Message: Track ended. */ + MPG123_NEW_FORMAT=-11, /**< Message: Output format will be different on next call. */ + MPG123_NEED_MORE=-10, /**< Message: For feed reader: "Feed me more!" */ + MPG123_ERR=-1, /**< Generic Error */ + MPG123_OK=0, /**< Success */ + MPG123_BAD_OUTFORMAT, /**< Unable to set up output format! */ + MPG123_BAD_CHANNEL, /**< Invalid channel number specified. */ + MPG123_BAD_RATE, /**< Invalid sample rate specified. */ + MPG123_ERR_16TO8TABLE, /**< Unable to allocate memory for 16 to 8 converter table! */ + MPG123_BAD_PARAM, /**< Bad parameter id! */ + MPG123_BAD_BUFFER, /**< Bad buffer given -- invalid pointer or too small size. */ + MPG123_OUT_OF_MEM, /**< Out of memory -- some malloc() failed. */ + MPG123_NOT_INITIALIZED, /**< You didn't initialize the library! */ + MPG123_BAD_DECODER, /**< Invalid decoder choice. */ + MPG123_BAD_HANDLE, /**< Invalid mpg123 handle. */ + MPG123_NO_BUFFERS, /**< Unable to initialize frame buffers (out of memory?). */ + MPG123_BAD_RVA, /**< Invalid RVA mode. */ + MPG123_NO_GAPLESS, /**< This build doesn't support gapless decoding. */ + MPG123_NO_SPACE, /**< Not enough buffer space. */ + MPG123_BAD_TYPES, /**< Incompatible numeric data types. */ + MPG123_BAD_BAND, /**< Bad equalizer band. */ + MPG123_ERR_NULL, /**< Null pointer given where valid storage address needed. */ + MPG123_ERR_READER, /**< Error reading the stream. */ + MPG123_NO_SEEK_FROM_END,/**< Cannot seek from end (end is not known). */ + MPG123_BAD_WHENCE, /**< Invalid 'whence' for seek function.*/ + MPG123_NO_TIMEOUT, /**< Build does not support stream timeouts. */ + MPG123_BAD_FILE, /**< File access error. */ + MPG123_NO_SEEK, /**< Seek not supported by stream. */ + MPG123_NO_READER, /**< No stream opened. */ + MPG123_BAD_PARS, /**< Bad parameter handle. */ + MPG123_BAD_INDEX_PAR, /**< Bad parameters to mpg123_index() */ + MPG123_OUT_OF_SYNC, /**< Lost track in bytestream and did not try to resync. */ + MPG123_RESYNC_FAIL /**< Resync failed to find valid MPEG data. */ +}; + +/** Return a string describing that error errcode means. */ +EXPORT const char* mpg123_plain_strerror(int errcode); + +/** Give string describing what error has occured in the context of handle mh. + * When a function operating on an mpg123 handle returns MPG123_ERR, you should check for the actual reason via + * char *errmsg = mpg123_strerror(mh) + * This function will catch mh == NULL and return the message for MPG123_BAD_HANDLE. */ +EXPORT const char* mpg123_strerror(mpg123_handle *mh); + +/** Return the plain errcode intead of a string. */ +EXPORT int mpg123_errcode(mpg123_handle *mh); + +/*@}*/ + + +/** \defgroup mpg123_decoder mpg123 decoder selection + * + * Functions to list and select the available decoders. + * Perhaps the most prominent feature of mpg123: You have several (optimized) decoders to choose from (on x86 and PPC (MacOS) systems, that is). + * + * @{ + */ + +/** Return a NULL-terminated array of generally available decoder names (plain 8bit ASCII). */ +EXPORT char **mpg123_decoders(); + +/** Return a NULL-terminated array of the decoders supported by the CPU (plain 8bit ASCII). */ +EXPORT char **mpg123_supported_decoders(); + +/** Set the chosen decoder to 'decoder_name' */ +EXPORT int mpg123_decoder(mpg123_handle *mh, const char* decoder_name); + +/*@}*/ + + +/** \defgroup mpg123_output mpg123 output audio format + * + * Functions to get and select the format of the decoded audio. + * + * @{ + */ + +/** 16 or 8 bits, signed or unsigned... all flags fit into 8 bits, float/double are not yet standard and special anyway */ +enum mpg123_enc_enum +{ + MPG123_ENC_16 = 0x40 /**< 0100 0000 Some 16 bit encoding... */ + ,MPG123_ENC_SIGNED = 0x80 /**< 1000 0000 Some signed encoding... */ + ,MPG123_ENC_8 = 0x0f /**< 0000 1111 Some 8 bit encoding... */ + ,MPG123_ENC_SIGNED_16 = (MPG123_ENC_16|MPG123_ENC_SIGNED|0x10) /**< 1101 0000 signed 16 bit */ + ,MPG123_ENC_UNSIGNED_16 = (MPG123_ENC_16|0x20) /**< 0110 0000 unsigned 16 bit*/ + ,MPG123_ENC_UNSIGNED_8 = 0x01 /**< 0000 0001 unsigned 8 bit*/ + ,MPG123_ENC_SIGNED_8 = (MPG123_ENC_SIGNED|0x02) /**< 1000 0010 signed 8 bit*/ + ,MPG123_ENC_ULAW_8 = 0x04 /**< 0000 0100 ulaw 8 bit*/ + ,MPG123_ENC_ALAW_8 = 0x08 /**< 0000 1000 alaw 8 bit */ + ,MPG123_ENC_ANY = ( MPG123_ENC_SIGNED_16 | MPG123_ENC_UNSIGNED_16 | MPG123_ENC_UNSIGNED_8 | MPG123_ENC_SIGNED_8 | MPG123_ENC_ULAW_8 | MPG123_ENC_ALAW_8 ) /**< any encoding */ +}; + +/** They can be combined into one number (3) to indicate mono and stereo... */ +enum mpg123_channelcount +{ + MPG123_MONO = 1 + ,MPG123_STEREO = 2 +}; + +/** An array of supported standard sample rates + * These are possible native sample rates of MPEG audio files. + * You can still force mpg123 to resample to a different one, but by default you will only get audio in one of these samplings. + * \param list Store a pointer to the sample rates array there. + * \param number Store the number of sample rates there. */ +EXPORT void mpg123_rates(const long **list, size_t *number); + +/** An array of supported audio encodings. + * An audio encoding is a logic OR of members of mpg123_enc_enum. + * \param list Store a pointer to the encodings array there. + * \param number Store the number of encodings there. */ +EXPORT void mpg123_encodings(const int **list, size_t *number); + +/** Configure a mpg123 handle to accept no output format at all, + * use before specifying supported formats with mpg123_format */ +EXPORT int mpg123_format_none(mpg123_handle *mh); + +/** Configure mpg123 handle to accept all formats + * (also any custom rate you may set) -- this is default. */ +EXPORT int mpg123_format_all(mpg123_handle *mh); + +/** Set the audio format support of a mpg123_handle in detail: + * \param mh audio decoder handle + * \param rate The sample rate value (in Hertz). + * \param channels A combination of MPG123_STEREO and MPG123_MONO. + * \param encodings A combination of accepted encodings for rate and channels, p.ex MPG123_ENC_SIGNED16 | MPG123_ENC_ULAW_8 (or 0 for no support). + * \return MPG123_OK on success, MPG123_ERR if there was an error. */ +EXPORT int mpg123_format(mpg123_handle *mh, long rate, int channels, int encodings); + +/** Check to see if a specific format at a specific rate is supported + * by mpg123_handle. + * \return 0 for no support (that includes invalid parameters), MPG123_STEREO, + * MPG123_MONO or MPG123_STEREO|MPG123_MONO. */ +EXPORT int mpg123_format_support(mpg123_handle *mh, long rate, int encoding); + +/** Get the current output format written to the addresses givenr. */ +EXPORT int mpg123_getformat(mpg123_handle *mh, long *rate, int *channels, int *encoding); + +/*@}*/ + + +/** \defgroup mpg123_input mpg123 file input and decoding + * + * Functions for input bitstream and decoding operations. + * + * @{ + */ + +/* reading samples / triggering decoding, possible return values: */ +/** Enumeration of the error codes returned by libmpg123 functions. */ + +/** Open and prepare to decode the specified file by filesystem path. + * This does not open HTTP urls; libmpg123 contains no networking code. + * If you want to decode internet streams, use mpg123_open_fd() or mpg123_open_feed(). + */ +EXPORT int mpg123_open(mpg123_handle *mh, char *path); + +/** Use an already opened file descriptor as the bitstream input + * mpg123_close() will _not_ close the file descriptor. + */ +EXPORT int mpg123_open_fd(mpg123_handle *mh, int fd); + +/** Open a new bitstream and prepare for direct feeding + * This works together with mpg123_decode(); you are responsible for reading and feeding the input bitstream. + */ +EXPORT int mpg123_open_feed(mpg123_handle *mh); + +/** Closes the source, if libmpg123 opened it. */ +EXPORT int mpg123_close(mpg123_handle *mh); + +/** Read from stream and decode up to outmemsize bytes. + * \param outmemory address of output buffer to write to + * \param outmemsize maximum number of bytes to write + * \param done address to store the number of actually decoded bytes to + * \return error/message code (watch out for MPG123_DONE and friends!) */ +EXPORT int mpg123_read(mpg123_handle *mh, unsigned char *outmemory, size_t outmemsize, size_t *done); + +/** Decode MPEG Audio from inmemory to outmemory. + * This is very close to a drop-in replacement for old mpglib. + * When you give zero-sized output buffer the input will be parsed until + * decoded data is available. This enables you to get MPG123_NEW_FORMAT (and query it) + * without taking decoded data. + * \param inmemory input buffer + * \param inmemsize number of input bytes + * \param outmemory output buffer + * \param outmemsize maximum number of output bytes + * \param done address to store the number of actually decoded bytes to + * \return error/message code (watch out especially for MPG123_NEED_MORE) + */ +EXPORT int mpg123_decode(mpg123_handle *mh, unsigned char *inmemory, size_t inmemsize, + unsigned char *outmemory, size_t outmemsize, size_t *done); + +/** Decode next MPEG frame to internal buffer + * or read a frame and return after setting a new format. + * \param num current frame offset gets stored there + * \param audio This pointer is set to the internal buffer to read the decoded audio from. + * \param bytes number of output bytes ready in the buffer + */ +EXPORT int mpg123_decode_frame(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes); + +/*@}*/ + + +/** \defgroup mpg123_seek mpg123 position and seeking + * + * Functions querying and manipulating position in the decoded audio bitstream. + * The position is measured in decoded audio samples, or MPEG frame offset for the specific functions. + * If gapless code is in effect, the positions are adjusted to compensate the skipped padding/delay - meaning, you should not care about that at all and just use the position defined for the samples you get out of the decoder;-) + * The general usage is modelled after stdlib's ftell() and fseek(). + * Especially, the whence parameter for the seek functions has the same meaning as the one for fseek() and needs the same constants from stdlib.h: + * - SEEK_SET: set position to (or near to) specified offset + * - SEEK_CUR: change position by offset from now + * - SEEK_END: set position to offset from end + * + * Note that sample-afccurate seek only works when gapless support has been enabled at compile time; seek is frame-accurate otherwise. + * Also, seeking is not guaranteed to work for all streams (underlying stream may not support it). + * + * @{ + */ + +/** Returns the current position in samples. + * On the next read, you'd get that sample. */ +EXPORT off_t mpg123_tell(mpg123_handle *mh); + +/** Returns the frame number that the next read will give you data from. */ +EXPORT off_t mpg123_tellframe(mpg123_handle *mh); + +/** Seek to a desired sample offset. + * Set whence to SEEK_SET, SEEK_CUR or SEEK_END. + * \return The resulting offset >= 0 or error/message code */ +EXPORT off_t mpg123_seek(mpg123_handle *mh, off_t sampleoff, int whence); + +/** Seek to a desired sample offset in data feeding mode. + * This just prepares things to be right only if you ensure that the next chunk of input data will be from input_offset byte position. + * \param input_offset The position it expects to be at the + * next time data is fed to mpg123_decode(). + * \return The resulting offset >= 0 or error/message code */ +EXPORT off_t mpg123_feedseek(mpg123_handle *mh, off_t sampleoff, int whence, off_t *input_offset); + +/** Seek to a desired MPEG frame index. + * Set whence to SEEK_SET, SEEK_CUR or SEEK_END. + * \return The resulting offset >= 0 or error/message code */ +EXPORT off_t mpg123_seek_frame(mpg123_handle *mh, off_t frameoff, int whence); + +/** Return a MPEG frame offset corresponding to an offset in seconds. + * This assumes that the samples per frame do not change in the file/stream, which is a good assumption for any sane file/stream only. + * \return frame offset >= 0 or error/message code */ +EXPORT off_t mpg123_timeframe(mpg123_handle *mh, double sec); + +/** Give access to the frame index table that is managed for seeking. + * You are asked not to modify the values... unless you are really aware of what you are doing. + * \param offsets pointer to the index array + * \param step one index byte offset advances this many MPEG frames + * \param fill number of recorded index offsets; size of the array */ +EXPORT int mpg123_index(mpg123_handle *mh, off_t **offsets, off_t *step, size_t *fill); + +/** Get information about current and remaining frames/seconds. + * WARNING: This function is there because of special usage by standalone mpg123 and may be removed in the final version of libmpg123! + * You provide an offset (in frames) from now and a number of output bytes + * served by libmpg123 but not yet played. You get the projected current frame + * and seconds, as well as the remaining frames/seconds. This does _not_ care + * about skipped samples due to gapless playback. */ +EXPORT int mpg123_position( mpg123_handle *mh, off_t frame_offset, + off_t buffered_bytes, off_t *current_frame, + off_t *frames_left, double *current_seconds, + double *seconds_left); + +/*@}*/ + + +/** \defgroup mpg123_voleq mpg123 volume and equalizer + * + * @{ + */ + +enum mpg123_channels +{ + MPG123_LEFT=0x1, /**< The Left Channel. */ + MPG123_RIGHT=0x2 /**< The Right Channel. */ +}; + +/** Set the 32 Band Audio Equalizer settings. + * \param channel Can be MPG123_LEFT, MPG123_RIGHT or MPG123_LEFT|MPG123_RIGHT for both. + * \param band The equaliser band to change (from 0 to 31) + * \param val The (linear) adjustment factor. */ +EXPORT int mpg123_eq(mpg123_handle *mh, enum mpg123_channels channel, int band, double val); + +/** Reset the 32 Band Audio Equalizer settings to flat */ +EXPORT int mpg123_reset_eq(mpg123_handle *mh); + +/** Set the absolute output volume including the RVA setting, + * vol<0 just applies (a possibly changed) RVA setting. */ +EXPORT int mpg123_volume(mpg123_handle *mh, double vol); + +/** Adjust output volume including the RVA setting by chosen amount */ +EXPORT int mpg123_volume_change(mpg123_handle *mh, double change); + +/** Return current volume setting, the actual value due to RVA, and the RVA + * adjustment itself. It's all as double float value to abstract the sample + * format. The volume values are linear factors / amplitudes (not percent) + * and the RVA value is in decibels. */ +EXPORT int mpg123_getvolume(mpg123_handle *mh, double *base, double *really, double *rva_db); + +/* TODO: Set some preamp in addition / to replace internal RVA handling? */ + +/*@}*/ + + +/** \defgroup mpg123_status mpg123 status and information + * + * @{ + */ + +/** Enumeration of the mode types of Variable Bitrate */ +enum mpg123_vbr { + MPG123_CBR=0, /**< Constant Bitrate Mode (default) */ + MPG123_VBR, /**< Variable Bitrate Mode */ + MPG123_ABR /**< Average Bitrate Mode */ +}; + +/** Enumeration of the MPEG Versions */ +enum mpg123_version { + MPG123_1_0=0, /**< MPEG Version 1.0 */ + MPG123_2_0, /**< MPEG Version 2.0 */ + MPG123_2_5 /**< MPEG Version 2.5 */ +}; + + +/** Enumeration of the MPEG Audio mode. + * Only the mono mode has 1 channel, the others have 2 channels. */ +enum mpg123_mode { + MPG123_M_STEREO=0, /**< Standard Stereo. */ + MPG123_M_JOINT, /**< Joint Stereo. */ + MPG123_M_DUAL, /**< Dual Channel. */ + MPG123_M_MONO /**< Single Channel. */ +}; + + +/** Enumeration of the MPEG Audio flag bits */ +enum mpg123_flags { + MPG123_CRC=0x1, /**< The bitstream is error protected using 16-bit CRC. */ + MPG123_COPYRIGHT=0x2, /**< The bitstream is copyrighted. */ + MPG123_PRIVATE=0x4, /**< The private bit has been set. */ + MPG123_ORIGINAL=0x8 /**< The bitstream is an original, not a copy. */ +}; + +/** Data structure for storing information about a frame of MPEG Audio */ +struct mpg123_frameinfo +{ + enum mpg123_version version; /**< The MPEG version (1.0/2.0/2.5). */ + int layer; /**< The MPEG Audio Layer (MP1/MP2/MP3). */ + long rate; /**< The sampling rate in Hz. */ + enum mpg123_mode mode; /**< The audio mode (Mono, Stereo, Joint-stero, Dual Channel). */ + int mode_ext; /**< The mode extension bit flag. */ + int framesize; /**< The size of the frame (in bytes). */ + enum mpg123_flags flags; /**< MPEG Audio flag bits. */ + int emphasis; /**< The emphasis type. */ + int bitrate; /**< Bitrate of the frame (kbps). */ + int abr_rate; /**< The target average bitrate. */ + enum mpg123_vbr vbr; /**< The VBR mode. */ +}; + +/** Get frame information about the MPEG audio bitstream and store it in a mpg123_frameinfo structure. */ +EXPORT int mpg123_info(mpg123_handle *mh, struct mpg123_frameinfo *mi); + +/** Get the safe output buffer size for all cases (when you want to replace the internal buffer) */ +EXPORT size_t mpg123_safe_buffer(); + +/** Make a full parsing scan of each frame in the file. ID3 tags are found. An accurate length + * value is stored. Seek index will be filled. A seek back to current position + * is performed. At all, this function refuses work when stream is + * not seekable. + * \return MPG123_OK or MPG123_ERR. + */ +EXPORT int mpg123_scan(mpg123_handle *mh); + +/** Return, if possible, the full (expected) length of current track in samples. */ +EXPORT off_t mpg123_length(mpg123_handle *mh); + +/** Returns the time (seconds) per frame; <0 is error. */ +EXPORT double mpg123_tpf(mpg123_handle *mh); + +/** Get and reset the clip count. */ +EXPORT long mpg123_clip(mpg123_handle *mh); + +/*@}*/ + + +/** \defgroup mpg123_metadata mpg123 metadata handling + * + * Functions to retrieve the metadata from MPEG Audio files and streams. + * Also includes string handling functions. + * + * @{ + */ + +/** Data structure for storing strings in a safer way than a standard C-String. + * Can also hold a number of null-terminated strings. */ +typedef struct +{ + char* p; /**< pointer to the string data */ + size_t size; /**< raw number of bytes allocated */ + size_t fill; /**< number of used bytes (including closing zero byte) */ +} mpg123_string; + +/** Create and allocate memory for a new mpg123_string */ +EXPORT void mpg123_init_string(mpg123_string* sb); + +/** Free-up mempory for an existing mpg123_string */ +EXPORT void mpg123_free_string(mpg123_string* sb); + +/** Change the size of a mpg123_string + * \return 0 on error, 1 on success */ +EXPORT int mpg123_resize_string(mpg123_string* sb, size_t news); + +/** Copy the contents of one mpg123_string string to another. + * \return 0 on error, 1 on success */ +EXPORT int mpg123_copy_string(mpg123_string* from, mpg123_string* to); + +/** Append a C-String to an mpg123_string + * \return 0 on error, 1 on success */ +EXPORT int mpg123_add_string(mpg123_string* sb, char* stuff); + +/** Set the conents of a mpg123_string to a C-String + * \return 0 on error, 1 on success */ +EXPORT int mpg123_set_string(mpg123_string* sb, char* stuff); + +/** Sub data structure for ID3v2, for storing various text fields (including comments). + * This is for ID3v2 COMM, TXXX and all the other text fields. + * Only COMM and TXXX have a description, only COMM has a language. + * You should consult the ID3v2 specification for the use of the various text fields ("frames" in ID3v2 documentation, I use "fields" here to separate from MPEG frames). */ +typedef struct +{ + char lang[3]; /**< Three-letter language code (not terminated). */ + char id[4]; /**< The ID3v2 text field id, like TALB, TPE2, ... (4 characters, no string termination). */ + mpg123_string description; /**< Empty for the generic comment... */ + mpg123_string text; /**< ... */ +} mpg123_text; + +/** Data structure for storing IDV3v2 tags. + * This structure is not a direct binary mapping with the file contents. + * The ID3v2 text frames are allowed to contain multiple strings. + * So check for null bytes until you reach the mpg123_string fill. + * All text is encoded in UTF-8. */ +typedef struct +{ + unsigned char version; /**< 3 or 4 for ID3v2.3 or ID3v2.4. */ + mpg123_string *title; /**< Title string (pointer into text_list). */ + mpg123_string *artist; /**< Artist string (pointer into text_list). */ + mpg123_string *album; /**< Album string (pointer into text_list). */ + mpg123_string *year; /**< The year as a string (pointer into text_list). */ + mpg123_string *genre; /**< Genre String (pointer into text_list). The genre string(s) may very well need postprocessing, esp. for ID3v2.3. */ + mpg123_string *comment; /**< Pointer to last encountered comment text with empty description. */ + /* Encountered ID3v2 fields are appended to these lists. + There can be multiple occurences, the pointers above always point to the last encountered data. */ + mpg123_text *comment_list; /**< Array of comments. */ + size_t comments; /**< Number of comments. */ + mpg123_text *text; /**< Array of ID3v2 text fields */ + size_t texts; /**< Numer of text fields. */ + mpg123_text *extra; /**< The array of extra (TXXX) fields. */ + size_t extras; /**< Number of extra text (TXXX) fields. */ +} mpg123_id3v2; + +/** Data structure for ID3v1 tags (the last 128 bytes of a file). + * Don't take anything for granted (like string termination)! + * Also note the change ID3v1.1 did: comment[28] = 0; comment[19] = track_number + * It is your task to support ID3v1 only or ID3v1.1 ...*/ +typedef struct +{ + char tag[3]; /**< Always the string "TAG", the classic intro. */ + char title[30]; /**< Title string. */ + char artist[30]; /**< Artist string. */ + char album[30]; /**< Album string. */ + char year[4]; /**< Year string. */ + char comment[30]; /**< Comment string. */ + unsigned char genre; /**< Genre index. */ +} mpg123_id3v1; + +#define MPG123_ID3 0x3 /**< 0011 There is some ID3 info. Also matches 0010 or NEW_ID3. */ +#define MPG123_NEW_ID3 0x1 /**< 0001 There is ID3 info that changed since last call to mpg123_id3. */ +#define MPG123_ICY 0xc /**< 1100 There is some ICY info. Also matches 0100 or NEW_ICY.*/ +#define MPG123_NEW_ICY 0x4 /**< 0100 There is ICY info that changed since last call to mpg123_icy. */ + +/** Query if there is (new) meta info, be it ID3 or ICY (or something new in future). + The check function returns a combination of flags. */ +EXPORT int mpg123_meta_check(mpg123_handle *mh); /* On error (no valid handle) just 0 is returned. */ + +/** Point v1 and v2 to existing data structures wich may change on any next read/decode function call. + * v1 and/or v2 can be set to NULL when there is no corresponding data. + * \return Return value is MPG123_OK or MPG123_ERR, */ +EXPORT int mpg123_id3(mpg123_handle *mh, mpg123_id3v1 **v1, mpg123_id3v2 **v2); + +/** Point icy_meta to existing data structure wich may change on any next read/decode function call. + * \return Return value is MPG123_OK or MPG123_ERR, */ +EXPORT int mpg123_icy(mpg123_handle *mh, char **icy_meta); /* same for ICY meta string */ + +/* @} */ + + +/** \defgroup mpg123_advpar mpg123 advanced parameter API + * + * Direct access to a parameter set without full handle around it. + * Possible uses: + * - Influence behaviour of library _during_ initialization of handle (MPG123_VERBOSE). + * - Use one set of parameters for multiple handles. + * + * The functions for handling mpg123_pars (mpg123_par() and mpg123_fmt() + * family) directly return a fully qualified mpg123 error code, the ones + * operating on full handles normally MPG123_OK or MPG123_ERR, storing the + * specific error code itseld inside the handle. + * + * @{ + */ + +/** Opaque structure for the libmpg123 decoder parameters. */ +struct mpg123_pars_struct; + +/** Opaque structure for the libmpg123 decoder parameters. */ +typedef struct mpg123_pars_struct mpg123_pars; + +/** Create a handle with preset parameters. */ +EXPORT mpg123_handle *mpg123_parnew(mpg123_pars *mp, const char* decoder, int *error); + +/** Allocate memory for and return a pointer to a new mpg123_pars */ +EXPORT mpg123_pars *mpg123_new_pars(int *error); + +/** Delete and free up memory used by a mpg123_pars data structure */ +EXPORT void mpg123_delete_pars(mpg123_pars* mp); + +/** Configure mpg123 parameters to accept no output format at all, + * use before specifying supported formats with mpg123_format */ +EXPORT int mpg123_fmt_none(mpg123_pars *mp); + +/** Configure mpg123 parameters to accept all formats + * (also any custom rate you may set) -- this is default. */ +EXPORT int mpg123_fmt_all(mpg123_pars *mp); + +/** Set the audio format support of a mpg123_pars in detail: + \param rate The sample rate value (in Hertz). + \param channels A combination of MPG123_STEREO and MPG123_MONO. + \param encodings A combination of accepted encodings for rate and channels, p.ex MPG123_ENC_SIGNED16|MPG123_ENC_ULAW_8 (or 0 for no support). + \return 0 on success, -1 if there was an error. / +*/ +EXPORT int mpg123_fmt(mpg123_pars *mh, long rate, int channels, int encodings); /* 0 is good, -1 is error */ + +/** Check to see if a specific format at a specific rate is supported + * by mpg123_pars. + * \return 0 for no support (that includes invalid parameters), MPG123_STEREO, + * MPG123_MONO or MPG123_STEREO|MPG123_MONO. */ +EXPORT int mpg123_fmt_support(mpg123_pars *mh, long rate, int encoding); + +/** Set a specific parameter, for a specific mpg123_pars, using a parameter + * type key chosen from the mpg123_parms enumeration, to the specified value. */ +EXPORT int mpg123_par(mpg123_pars *mp, enum mpg123_parms type, long value, double fvalue); + +/** Get a specific parameter, for a specific mpg123_pars. + * See the mpg123_parms enumeration for a list of available parameters. */ +EXPORT int mpg123_getpar(mpg123_pars *mp, enum mpg123_parms type, long *val, double *fval); + +/* @} */ + + +/** \defgroup mpg123_lowio mpg123 low level I/O + * You may want to do tricky stuff with I/O that does not work with mpg123's default file access or you want to make it decode into your own pocket... + * + * @{ */ + +/** Replace default internal buffer with user-supplied buffer. + * Instead of working on it's own private buffer, mpg123 will directly use the one you provide for storing decoded audio. */ +EXPORT int mpg123_replace_buffer(mpg123_handle *mh, unsigned char *data, size_t size); + +/** The max size of one frame's decoded output with current settings. + * Use that to determine an appropriate minimum buffer size for decoding one frame. */ +EXPORT size_t mpg123_outblock(mpg123_handle *mh); + +/** Replace low-level stream access functions; read and lseek as known in POSIX. + * You can use this to make any fancy file opening/closing yourself, + * using open_fd to set the file descriptor for your read/lseek (doesn't need to be a "real" file descriptor...). + * Setting a function to NULL means that the default internal read is + * used (active from next mpg123_open call on). */ +EXPORT int mpg123_replace_reader( mpg123_handle *mh, + ssize_t (*r_read) (int, void *, size_t), + off_t (*r_lseek)(int, off_t, int) ); + +/* @} */ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext_libs/Makefile.mingw b/ext_libs/Makefile.mingw index 4991a176..bc77caa6 100644 --- a/ext_libs/Makefile.mingw +++ b/ext_libs/Makefile.mingw @@ -4,5 +4,8 @@ export DLLTOOL=i586-mingw32msvc-dlltool libvorbis.a: libvorbis.def $(DLLTOOL) -d libvorbis.def -l libvorbis.a +libmpg123-0.a: libmpg123-0.def + $(DLLTOOL) -d libmpg123-0.def -l libmpg123-0.a + clean: - rm -f libvorbis.a + rm -f libvorbis.a libmpg123-0.a diff --git a/ext_libs/ext_libs.vcproj b/ext_libs/ext_libs.vcproj index 1e30bf2c..5897b209 100644 --- a/ext_libs/ext_libs.vcproj +++ b/ext_libs/ext_libs.vcproj @@ -82,6 +82,30 @@ /> + + + + + + + + diff --git a/ext_libs/libmpg123-0.def b/ext_libs/libmpg123-0.def new file mode 100755 index 00000000..e713560c --- /dev/null +++ b/ext_libs/libmpg123-0.def @@ -0,0 +1,68 @@ +LIBRARY libmpg123-0.dll +EXPORTS +mpg123_init +mpg123_exit +mpg123_new +mpg123_delete +mpg123_plain_strerror +mpg123_strerror +mpg123_errcode +mpg123_decoders +mpg123_supported_decoders +mpg123_decoder +mpg123_rates +mpg123_encodings +mpg123_format_none +mpg123_format_all +mpg123_format +mpg123_format_support +mpg123_getformat +mpg123_open +mpg123_open_feed +mpg123_open_fd +mpg123_close +mpg123_replace_reader +mpg123_tell +mpg123_tellframe +mpg123_position +mpg123_read +mpg123_decode +mpg123_decode_frame +mpg123_seek +mpg123_feedseek +mpg123_seek_frame +mpg123_timeframe +mpg123_index +mpg123_eq +mpg123_reset_eq +mpg123_volume +mpg123_volume_change +mpg123_getvolume +mpg123_info +mpg123_safe_buffer +mpg123_outblock +mpg123_replace_buffer +mpg123_scan +mpg123_length +mpg123_tpf +mpg123_clip +mpg123_init_string +mpg123_free_string +mpg123_resize_string +mpg123_copy_string +mpg123_add_string +mpg123_set_string +mpg123_meta_check +mpg123_id3 +mpg123_icy +mpg123_parnew +mpg123_new_pars +mpg123_delete_pars +mpg123_param +mpg123_getparam +mpg123_fmt_none +mpg123_fmt_all +mpg123_fmt +mpg123_fmt_support +mpg123_par +mpg123_getpar diff --git a/readme.txt b/readme.txt index ca2ff811..d324cc32 100644 --- a/readme.txt +++ b/readme.txt @@ -1,20 +1,23 @@ vgmstream This is vgmstream, a library for playing streamed audio from video games. -It is very much under development. There are two end-user bits, a command +It is very much under development. There are two end-user bits: a command line decoder called "test", and a simple Winamp plugin called "in_vgmstream". --- needed files (for Windows) --- -Since Ogg Vorbis is now supported, you will need to have libvorbis.dll. -I suggest this one: +Since Ogg Vorbis and MPEG audio are now supported, you will need to have +libvorbis.dll and libmpg123-0.dll. +I suggest getting libvorbis.dll here: http://www.rarewares.org/files/ogg/libvorbis1.2.0.zip -and the companion Intel math dll, if you need it: +and the companion Intel math dll: http://www.rarewares.org/files/libmmd9.1.zip +And libmpg123-0.dll from this archive: +http://www.mpg123.de/download/win32/mpg123-1.4.3-x86.zip -Put libvorbis.dll and libmmd.dll somewhere Windows can find them. For -in_vgmstream this means in the directory with winamp.exe, or in a system -directory. For test.exe that means in this means in the same directory as -test.exe, or in a system directory. +Put libvorbis.dll, libmmd.dll, and libmpg123-0.dll somewhere Windows can find +them. For in_vgmstream this means in the directory with winamp.exe, or in a +system directory. For test.exe this means in the same directory as test.exe, +or in a system directory. --- test.exe --- Usage: test.exe [-o outfile.wav] [-l loop count] @@ -94,6 +97,7 @@ File types supported by this version of vgmstream: - .aiff (8/16 bit PCM) - .str (SDX2 DPCM) - .aud (IMA ADPCM, WS DPCM) +- .ahx (MPEG-2 Layer II) Enjoy! -hcs diff --git a/src/Makefile b/src/Makefile index f6fa02e0..1cbc03c1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -10,7 +10,8 @@ CODING_OBJS=coding/adx_decoder.o \ coding/eaxa_decoder.o \ coding/ogg_vorbis_decoder.o \ coding/sdx2_decoder.o \ - coding/ws_decoder.o + coding/ws_decoder.o \ + coding/mpeg_decoder.o LAYOUT_OBJS=layout/ast_blocked.o \ layout/blocked.o \ @@ -63,7 +64,8 @@ META_OBJS=meta/adx_header.o \ meta/ps2_bmdx.o \ meta/aifc.o \ meta/str_snds.o \ - meta/ws_aud.o + meta/ws_aud.o \ + meta/ahx.o OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) diff --git a/src/coding/Makefile.unix.am b/src/coding/Makefile.unix.am index 53439127..bbc69e01 100644 --- a/src/coding/Makefile.unix.am +++ b/src/coding/Makefile.unix.am @@ -4,6 +4,6 @@ AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) AM_MAKEFLAGS=-f Makefile.unix libcoding_la_LDFLAGS = -libcoding_la_SOURCES = adx_decoder.c eaxa_decoder.c g721_decoder.c ima_decoder.c ngc_afc_decoder.c ngc_dsp_decoder.c ngc_dtk_decoder.c pcm_decoder.c psx_decoder.c xa_decoder.c ogg_vorbis_decoder.c sdx2_decoder.c ws_decoder.c +libcoding_la_SOURCES = adx_decoder.c eaxa_decoder.c g721_decoder.c ima_decoder.c ngc_afc_decoder.c ngc_dsp_decoder.c ngc_dtk_decoder.c pcm_decoder.c psx_decoder.c xa_decoder.c ogg_vorbis_decoder.c sdx2_decoder.c ws_decoder.c mpeg_decoder.c EXTRA_DIST = coding.h g72x_state.h diff --git a/src/coding/coding.h b/src/coding/coding.h index f29fd4e0..1f0f6fc8 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -41,4 +41,10 @@ void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +#ifdef VGM_USE_MPEG +void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream, + fake_mpeg2_l2_codec_data * data, + sample * outbuf, int32_t samples_to_do); +#endif + #endif diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c new file mode 100644 index 00000000..8872175f --- /dev/null +++ b/src/coding/mpeg_decoder.c @@ -0,0 +1,88 @@ +#include "../vgmstream.h" + +#ifdef VGM_USE_MPEG +#include +#include +#include "coding.h" +#include "../util.h" + +/* mono, mpg123 expects frames of 0x414 (160kbps, 22050Hz) but they + * actually vary and are much shorter */ +void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL *stream, + fake_mpeg2_l2_codec_data * data, + sample * outbuf, int32_t samples_to_do) { + int samples_done = 0; + + while (samples_done < samples_to_do) { + size_t bytes_done; + int rc; + + if (!data->buffer_full) { + /* fill buffer up to next frame ending (or file ending) */ + int bytes_into_header = 0; + const uint8_t header[4] = {0xff,0xf5,0xe0,0xc0}; + off_t frame_offset = 0; + + /* assume that we are starting at a header, skip it and look for the + * next one */ + read_streamfile(data->buffer, stream->offset+frame_offset, 4, + stream->streamfile); + frame_offset += 4; + + do { + uint8_t byte; + byte = + read_8bit(stream->offset+frame_offset,stream->streamfile); + data->buffer[frame_offset] = byte; + frame_offset++; + + if (byte == header[bytes_into_header]) { + bytes_into_header++; + } else { + /* This might have been the first byte of the header, so + * we need to check again. + * No need to get more complicated than this, though, since + * there are no repeated characters in the search string. */ + if (bytes_into_header>0) { + frame_offset--; + } + bytes_into_header=0; + } + + if (bytes_into_header==4) { + break; + } + } while (frame_offset < AHX_EXPECTED_FRAME_SIZE); + + if (bytes_into_header==4) frame_offset-=4; + memset(data->buffer+frame_offset,0, + AHX_EXPECTED_FRAME_SIZE-frame_offset); + + data->buffer_full = 1; + data->buffer_used = 0; + + stream->offset += frame_offset; + } + + if (!data->buffer_used) { + rc = mpg123_decode(data->m, + data->buffer,AHX_EXPECTED_FRAME_SIZE, + (unsigned char *)(outbuf+samples_done), + (samples_to_do-samples_done)*sizeof(sample), + &bytes_done); + data->buffer_used = 1; + } else { + rc = mpg123_decode(data->m, + NULL,0, + (unsigned char *)(outbuf+samples_done), + (samples_to_do-samples_done)*sizeof(sample), + &bytes_done); + } + + if (rc == MPG123_NEED_MORE) data->buffer_full = 0; + + samples_done += bytes_done/sizeof(sample); + } +} + +#endif diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index 5d3aa9af..3229d82a 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -4,6 +4,6 @@ AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir) AM_MAKEFLAGS=-f Makefile.unix libmeta_la_LDFLAGS = -libmeta_la_SOURCES = Cstr.c adx_header.c afc_header.c agsc.c ast.c brstm.c ea_header.c gcsw.c halpst.c nds_strm.c ngc_adpdtk.c ngc_caf.c ngc_dsp_std.c ps2_ads.c ps2_exst.c ps2_ild.c ps2_int.c ps2_mib.c ps2_mic.c ps2_npsf.c ps2_pnb.c ps2_rxw.c ps2_str.c ps2_svag.c ps2_vag.c ps2_vpk.c psx_cdxa.c raw.c rs03.c rsf.c rwsd.c psx_gms.c xbox_xwav.c xbox_wavm.c genh.c ogg_vorbis_file.c ps2_bmdx.c aifc.c str_snds.c ws_aud.c +libmeta_la_SOURCES = Cstr.c adx_header.c afc_header.c agsc.c ast.c brstm.c ea_header.c gcsw.c halpst.c nds_strm.c ngc_adpdtk.c ngc_caf.c ngc_dsp_std.c ps2_ads.c ps2_exst.c ps2_ild.c ps2_int.c ps2_mib.c ps2_mic.c ps2_npsf.c ps2_pnb.c ps2_rxw.c ps2_str.c ps2_svag.c ps2_vag.c ps2_vpk.c psx_cdxa.c raw.c rs03.c rsf.c rwsd.c psx_gms.c xbox_xwav.c xbox_wavm.c genh.c ogg_vorbis_file.c ps2_bmdx.c aifc.c str_snds.c ws_aud.c ahx.c EXTRA_DIST = meta.h diff --git a/src/meta/ahx.c b/src/meta/ahx.c new file mode 100644 index 00000000..c6491054 --- /dev/null +++ b/src/meta/ahx.c @@ -0,0 +1,121 @@ +#include "../vgmstream.h" + +#ifdef VGM_USE_MPEG + +#include "meta.h" +#include "../util.h" + +/* AHX is a CRI format which contains an MPEG-2 Layer 2 audio stream. + * Although the MPEG frame headers are incorrect... */ + +VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + off_t stream_offset; + size_t filesize; + char filename[260]; + int channel_count = 1; + int loop_flag = 0; + fake_mpeg2_l2_codec_data *data = NULL; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("ahx",filename_extension(filename))) goto fail; + + filesize = get_streamfile_size(streamFile); + + /* check first 2 bytes */ + if ((uint16_t)read_16bitBE(0,streamFile)!=0x8000) goto fail; + + /* get stream offset, check for CRI signature just before */ + stream_offset = (uint16_t)read_16bitBE(2,streamFile) + 4; + if ((uint16_t)read_16bitBE(stream_offset-6,streamFile)!=0x2863 ||/* "(c" */ + (uint32_t)read_32bitBE(stream_offset-4,streamFile)!=0x29435249 /* ")CRI" */ + ) goto fail; + + /* check for encoding type */ + /* 2 is for some unknown fixed filter, 3 is standard ADX, 4 is + * ADX with exponential scale, 0x11 is AHX */ + /* Sappharad reports that old AHXs (Sonic Adventure 2) don't have this flag. + * When I see one perhaps I can add an exception for those. */ + if (read_8bit(4,streamFile) != 0x11) goto fail; + + /* check for frame size (0 for AHX) */ + if (read_8bit(5,streamFile) != 0) goto fail; + + /* check for bits per sample? (0 for AHX) */ + if (read_8bit(6,streamFile) != 0) goto fail; + + /* check channel count (only mono AHXs are known) */ + if (read_8bit(7,streamFile) != 1) goto fail; + + /* At this point we almost certainly have an ADX file, + * so let's build the VGMSTREAM. */ + + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->num_samples = read_32bitBE(0xc,streamFile); + /* This is the One True Samplerate, the MPEG headers lie. */ + vgmstream->sample_rate = read_32bitBE(8,streamFile); + + vgmstream->coding_type = coding_fake_MPEG2_L2; + vgmstream->layout_type = layout_fake_mpeg; + vgmstream->meta_type = meta_AHX; + + { + int i; + STREAMFILE * chstreamfile; + + chstreamfile = streamFile->open(streamFile,filename, + STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!chstreamfile) goto fail; + + for (i=0;ich[i].streamfile = chstreamfile; + + vgmstream->ch[i].channel_start_offset= + vgmstream->ch[i].offset= + stream_offset; + } + } + + /* ooh, fun part, set up mpg123 */ + { + int rc; + data = calloc(1,sizeof(fake_mpeg2_l2_codec_data)); + if (!data) goto fail; + + data->m = mpg123_new(NULL,&rc); + if (rc==MPG123_NOT_INITIALIZED) { + if (mpg123_init()!=MPG123_OK) goto fail; + data->m = mpg123_new(NULL,&rc); + if (rc!=MPG123_OK) goto fail; + } else if (rc!=MPG123_OK) { + goto fail; + } + + if (mpg123_open_feed(data->m)!=MPG123_OK) { + goto fail; + } + + vgmstream->codec_data = data; + } + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (data) { + if (data->m) { + mpg123_delete(data->m); + data->m = NULL; + } + free(data); + data = NULL; + } + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} + +#endif diff --git a/src/meta/meta.h b/src/meta/meta.h index 20970010..2bb8516a 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -97,4 +97,8 @@ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_ws_aud(STREAMFILE * streamFile); +#ifdef VGM_USE_MPEG +VGMSTREAM * init_vgmstream_ahx(STREAMFILE * streamFile); +#endif + #endif diff --git a/src/vgmstream.c b/src/vgmstream.c index fce2a86f..c67e5e66 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -10,7 +10,6 @@ #include "layout/layout.h" #include "coding/coding.h" - /* * List of functions that will recognize files. These should correspond pretty * directly to the metadata types @@ -64,6 +63,9 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_aifc, init_vgmstream_str_snds, init_vgmstream_ws_aud, +#ifdef VGM_USE_MPEG + init_vgmstream_ahx, +#endif }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) @@ -137,13 +139,23 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { #ifdef VGM_USE_VORBIS if (vgmstream->meta_type==meta_ogg_vorbis) { - ogg_vorbis_codec_data *data = - (ogg_vorbis_codec_data *)(vgmstream->codec_data); + ogg_vorbis_codec_data *data = vgmstream->codec_data; + OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); ov_pcm_seek(ogg_vorbis_file, 0); } #endif +#ifdef VGM_USE_MPEG + if (vgmstream->coding_type==coding_fake_MPEG2_L2) { + off_t input_offset; + fake_mpeg2_l2_codec_data *data = vgmstream->codec_data; + + /* input_offset is ignored as we can assume it will be 0 for a seek + * to sample 0 */ + mpg123_feedseek(data->m,0,SEEK_SET,&input_offset); + } +#endif } /* simply allocate memory for the VGMSTREAM and its channels */ @@ -230,12 +242,10 @@ void close_vgmstream(VGMSTREAM * vgmstream) { #ifdef VGM_USE_VORBIS if (vgmstream->meta_type==meta_ogg_vorbis) { - ogg_vorbis_codec_data *data = - (ogg_vorbis_codec_data *)(vgmstream->codec_data); + ogg_vorbis_codec_data *data = vgmstream->codec_data; if (vgmstream->codec_data) { OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); - ov_clear(ogg_vorbis_file); close_streamfile(data->ov_streamfile.streamfile); @@ -245,6 +255,23 @@ void close_vgmstream(VGMSTREAM * vgmstream) { } #endif +#ifdef VGM_USE_MPEG + if (vgmstream->coding_type==coding_fake_MPEG2_L2) { + fake_mpeg2_l2_codec_data *data = vgmstream->codec_data; + + if (data) { + mpg123_delete(data->m); + free(vgmstream->codec_data); + vgmstream->codec_data = NULL; + /* The astute reader will note that a call to mpg123_exit is never + * make. While is is evilly breaking our contract with mpg123, it + * doesn't actually do anything except set the "initialized" flag + * to 0. And if we exit we run the risk of turning it off when + * someone else in another thread is using it. */ + } + } +#endif + free(vgmstream); } @@ -262,6 +289,9 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre break; #ifdef VGM_USE_VORBIS case layout_ogg_vorbis: +#endif +#ifdef VGM_USE_MPEG + case layout_fake_mpeg: #endif case layout_dtk_interleave: case layout_none: @@ -291,6 +321,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_PCM8: #ifdef VGM_USE_VORBIS case coding_ogg_vorbis: +#endif +#ifdef VGM_USE_MPEG + case coding_fake_MPEG2_L2: #endif case coding_SDX2: return 1; @@ -515,6 +548,16 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } break; +#ifdef VGM_USE_MPEG + case coding_fake_MPEG2_L2: + for (chan=0;chanchannels;chan++) { + decode_fake_mpeg2_l2( + &vgmstream->ch[chan], + vgmstream->codec_data, + buffer+samples_written*vgmstream->channels,samples_to_do); + } + break; +#endif } } @@ -713,6 +756,11 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case coding_WS: snprintf(temp,TEMPSIZE,"Westwood Studios DPCM"); break; +#ifdef VGM_USE_MPEG + case coding_fake_MPEG2_L2: + snprintf(temp,TEMPSIZE,"MPEG-2 Layer II Audio"); + break; +#endif default: snprintf(temp,TEMPSIZE,"CANNOT DECODE"); } @@ -763,6 +811,12 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case layout_ws_aud_blocked: snprintf(temp,TEMPSIZE,"Westwood Studios .aud blocked"); break; +#ifdef VGM_USE_MPEG + case layout_fake_mpeg: + snprintf(temp,TEMPSIZE,"MPEG Audio stream with incorrect frame headers"); + break; +#endif + default: snprintf(temp,TEMPSIZE,"INCONCEIVABLE"); } @@ -969,6 +1023,11 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case meta_WS_AUD_old: snprintf(temp,TEMPSIZE,"Westwood Studios .aud (old) header"); break; +#ifdef VGM_USE_MPEG + case meta_AHX: + snprintf(temp,TEMPSIZE,"CRI AHX header"); + break; +#endif default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); } diff --git a/src/vgmstream.h b/src/vgmstream.h index ef3e26bf..51ee99ca 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -5,13 +5,21 @@ #ifndef _VGMSTREAM_H #define _VGMSTREAM_H +/* Vorbis and MPEG decoding are done by external libraries. + * If someone wants to do a standalone build, they can do it by simply + * removing these defines (and the references to the libraries in the + * Makefile) */ #define VGM_USE_VORBIS +#define VGM_USE_MPEG #include "streamfile.h" #include "coding/g72x_state.h" #ifdef VGM_USE_VORBIS #include #endif +#ifdef VGM_USE_MPEG +#include +#endif /* The encoding type specifies the format the sound data itself takes */ typedef enum { @@ -39,6 +47,9 @@ typedef enum { coding_DVI_IMA, /* DVI (bare IMA, high nibble first), aka ADP4 */ coding_IMA, /* bare IMA, low nibble first */ coding_WS, /* Westwood Studios' custom VBR ADPCM */ +#ifdef VGM_USE_MPEG + coding_fake_MPEG2_L2, /* MPEG-2 Level 2 (AHX), with lying headers */ +#endif } coding_t; /* The layout type specifies how the sound data is laid out in the file */ @@ -68,6 +79,9 @@ typedef enum { #ifdef VGM_USE_VORBIS layout_ogg_vorbis, /* ogg vorbis file */ #endif +#ifdef VGM_USE_MPEG + layout_fake_mpeg, /* MPEG audio stream with bad frame headers (AHX) */ +#endif } layout_t; /* The meta type specifies how we know what we know about the file. We may know because of a header we read, some of it may have been guessed from filenames, etc. */ @@ -149,6 +163,9 @@ typedef enum { meta_STR_SNDS, /* .str with SNDS blocks and SHDR header */ meta_WS_AUD, /* Westwood Studios .aud */ meta_WS_AUD_old, /* Westwood Studios .aud, old style */ +#ifdef VGM_USE_MPEG + meta_AHX, /* CRI AHX header (same structure as ADX) */ +#endif } meta_t; typedef struct { @@ -259,6 +276,15 @@ typedef struct { } ogg_vorbis_codec_data; #endif +#ifdef VGM_USE_MPEG +#define AHX_EXPECTED_FRAME_SIZE 0x414 +typedef struct { + uint8_t buffer[AHX_EXPECTED_FRAME_SIZE]; + int buffer_used; + int buffer_full; + mpg123_handle *m; +} fake_mpeg2_l2_codec_data; +#endif /* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */ VGMSTREAM * init_vgmstream(const char * const filename); diff --git a/test/Makefile b/test/Makefile index c2cfbca2..892f95b9 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,6 +1,6 @@ export SHELL = /bin/sh export CFLAGS=-Wall -O3 -export LDFLAGS=-lm -L../src -lvgmstream -lvorbisfile +export LDFLAGS=-lm -L../src -lvgmstream -lvorbisfile -lmpg123 export STRIP=strip .PHONY: libvgmstream.a diff --git a/test/Makefile.mingw b/test/Makefile.mingw index e7e51038..af160176 100644 --- a/test/Makefile.mingw +++ b/test/Makefile.mingw @@ -1,13 +1,13 @@ export SHELL = /bin/sh export CFLAGS=-Wall -O3 -I../ext_includes -export LDFLAGS=-lm -L../src -L../ext_libs -lvgmstream -lvorbis +export LDFLAGS=-lm -L../src -L../ext_libs -lvgmstream -lvorbis -lmpg123-0 export CC=i586-mingw32msvc-gcc export AR=i586-mingw32msvc-ar export STRIP=i586-mingw32msvc-strip -.PHONY: libvgmstream.a libvorbis.a +.PHONY: libvgmstream.a libvorbis.a libmpg123-0.a -test.exe: libvgmstream.a libvorbis.a +test.exe: libvgmstream.a libvorbis.a libmpg123-0.a $(CC) $(CFLAGS) "-DVERSION=\"`../version.sh`\"" test.c $(LDFLAGS) -o test.exe $(STRIP) test.exe @@ -17,5 +17,8 @@ libvgmstream.a: libvorbis.a: $(MAKE) -C ../ext_libs -f Makefile.mingw $@ +libmpg123-0.a: + $(MAKE) -C ../ext_libs -f Makefile.mingw $@ + clean: rm -f test.exe diff --git a/unix/data.c b/unix/data.c index b748728c..2dd8e5ca 100644 --- a/unix/data.c +++ b/unix/data.c @@ -63,6 +63,7 @@ gchar *vgmstream_exts [] = { "wsi", "aifc", "aud", + "ahx", /* terminator */ NULL }; diff --git a/winamp/Makefile b/winamp/Makefile index 5507afb5..bb32262d 100644 --- a/winamp/Makefile +++ b/winamp/Makefile @@ -1,14 +1,14 @@ export SHELL = /bin/sh export CFLAGS=-Wall -O3 -I../ext_includes -export LDFLAGS=-lm -L../src -L../ext_libs -lvgmstream -lvorbis +export LDFLAGS=-lm -L../src -L../ext_libs -lvgmstream -lvorbis -lmpg123-0 export CC=i586-mingw32msvc-gcc export AR=i586-mingw32msvc-ar export STRIP=i586-mingw32msvc-strip export WINDRES=i586-mingw32msvc-windres -.PHONY: libvgmstream.a libvorbis.a +.PHONY: libvgmstream.a libvorbis.a libmpg123-0.a -in_vgmstream.dll: libvgmstream.a libvorbis.a in_vgmstream.c resource.o +in_vgmstream.dll: libvgmstream.a libvorbis.a libmpg123-0.a in_vgmstream.c resource.o $(CC) -shared $(CFLAGS) "-DVERSION=\"`../version.sh`\"" in_vgmstream.c resource.o $(LDFLAGS) -o in_vgmstream.dll $(STRIP) in_vgmstream.dll @@ -21,5 +21,8 @@ libvgmstream.a: libvorbis.a: $(MAKE) -C ../ext_libs -f Makefile.mingw libvorbis.a +libmpg123-0.a: + $(MAKE) -C ../ext_libs -f Makefile.mingw libmpg123-0.a + clean: rm -f in_vgmstream.dll resource.o diff --git a/winamp/in_vgmstream.c b/winamp/in_vgmstream.c index 1258380b..fd6fa3b0 100644 --- a/winamp/in_vgmstream.c +++ b/winamp/in_vgmstream.c @@ -121,6 +121,7 @@ char * extension_list[] = { "wsi\0WSI Audio File (*.WSI)\0", "aifc\0AIFC Audio File (*.AIFC)\0", "aud\0AUD Audio File (*.AUD)\0", + "ahx\0AHX Audio File (*.AHX)\0", }; void about(HWND hwndParent) {