commit
a9c8d1e3ea
13 changed files with 1495 additions and 0 deletions
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
# Copyright (C) 2019 Alexandros Theodotou <alex at zrythm dot org> |
||||
# |
||||
# This file is part of libaudec |
||||
# |
||||
# libaudec is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU Lesser General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# libaudec is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License |
||||
# along with libaudec. If not, see <https://www.gnu.org/licenses/>. |
||||
|
||||
# Build files |
||||
build |
||||
builddir |
||||
_build |
||||
|
||||
# -- C++ |
||||
# Prerequisites |
||||
*.d |
||||
|
||||
# Compiled Object files |
||||
*.slo |
||||
*.lo |
||||
*.o |
||||
*.obj |
||||
**/*/*.so* |
||||
|
||||
# Precompiled Headers |
||||
*.gch |
||||
*.pch |
||||
|
||||
# Compiled Dynamic libraries |
||||
*.so |
||||
*.dylib |
||||
build/*.dll |
||||
|
||||
# Fortran module files |
||||
*.mod |
||||
*.smod |
||||
|
||||
# Compiled Static libraries |
||||
*.lai |
||||
*.la |
||||
*.a |
||||
*.lib |
||||
|
||||
# Executables |
||||
*.exe |
||||
*.out |
||||
*.app |
||||
|
||||
# -- Vim |
||||
# Swap |
||||
[._]*.s[a-v][a-z] |
||||
[._]*.sw[a-p] |
||||
[._]s[a-rt-v][a-z] |
||||
[._]ss[a-gi-z] |
||||
[._]sw[a-p] |
||||
.vimrc |
||||
|
||||
# Session |
||||
Session.vim |
||||
|
||||
# Temporary |
||||
.netrwhist |
||||
*~ |
||||
*.ui# |
||||
**/.deps |
||||
|
||||
# Auto-generated tag files |
||||
tags |
||||
# Persistent undo |
||||
[._]*.un~ |
||||
|
||||
# Backup files |
||||
*.bak |
||||
|
||||
# Valgrind |
||||
vgcore.* |
@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE |
||||
Version 3, 29 June 2007 |
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> |
||||
Everyone is permitted to copy and distribute verbatim copies |
||||
of this license document, but changing it is not allowed. |
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates |
||||
the terms and conditions of version 3 of the GNU General Public |
||||
License, supplemented by the additional permissions listed below. |
||||
|
||||
0. Additional Definitions. |
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser |
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU |
||||
General Public License. |
||||
|
||||
"The Library" refers to a covered work governed by this License, |
||||
other than an Application or a Combined Work as defined below. |
||||
|
||||
An "Application" is any work that makes use of an interface provided |
||||
by the Library, but which is not otherwise based on the Library. |
||||
Defining a subclass of a class defined by the Library is deemed a mode |
||||
of using an interface provided by the Library. |
||||
|
||||
A "Combined Work" is a work produced by combining or linking an |
||||
Application with the Library. The particular version of the Library |
||||
with which the Combined Work was made is also called the "Linked |
||||
Version". |
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the |
||||
Corresponding Source for the Combined Work, excluding any source code |
||||
for portions of the Combined Work that, considered in isolation, are |
||||
based on the Application, and not on the Linked Version. |
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the |
||||
object code and/or source code for the Application, including any data |
||||
and utility programs needed for reproducing the Combined Work from the |
||||
Application, but excluding the System Libraries of the Combined Work. |
||||
|
||||
1. Exception to Section 3 of the GNU GPL. |
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License |
||||
without being bound by section 3 of the GNU GPL. |
||||
|
||||
2. Conveying Modified Versions. |
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a |
||||
facility refers to a function or data to be supplied by an Application |
||||
that uses the facility (other than as an argument passed when the |
||||
facility is invoked), then you may convey a copy of the modified |
||||
version: |
||||
|
||||
a) under this License, provided that you make a good faith effort to |
||||
ensure that, in the event an Application does not supply the |
||||
function or data, the facility still operates, and performs |
||||
whatever part of its purpose remains meaningful, or |
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of |
||||
this License applicable to that copy. |
||||
|
||||
3. Object Code Incorporating Material from Library Header Files. |
||||
|
||||
The object code form of an Application may incorporate material from |
||||
a header file that is part of the Library. You may convey such object |
||||
code under terms of your choice, provided that, if the incorporated |
||||
material is not limited to numerical parameters, data structure |
||||
layouts and accessors, or small macros, inline functions and templates |
||||
(ten or fewer lines in length), you do both of the following: |
||||
|
||||
a) Give prominent notice with each copy of the object code that the |
||||
Library is used in it and that the Library and its use are |
||||
covered by this License. |
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license |
||||
document. |
||||
|
||||
4. Combined Works. |
||||
|
||||
You may convey a Combined Work under terms of your choice that, |
||||
taken together, effectively do not restrict modification of the |
||||
portions of the Library contained in the Combined Work and reverse |
||||
engineering for debugging such modifications, if you also do each of |
||||
the following: |
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that |
||||
the Library is used in it and that the Library and its use are |
||||
covered by this License. |
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license |
||||
document. |
||||
|
||||
c) For a Combined Work that displays copyright notices during |
||||
execution, include the copyright notice for the Library among |
||||
these notices, as well as a reference directing the user to the |
||||
copies of the GNU GPL and this license document. |
||||
|
||||
d) Do one of the following: |
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this |
||||
License, and the Corresponding Application Code in a form |
||||
suitable for, and under terms that permit, the user to |
||||
recombine or relink the Application with a modified version of |
||||
the Linked Version to produce a modified Combined Work, in the |
||||
manner specified by section 6 of the GNU GPL for conveying |
||||
Corresponding Source. |
||||
|
||||
1) Use a suitable shared library mechanism for linking with the |
||||
Library. A suitable mechanism is one that (a) uses at run time |
||||
a copy of the Library already present on the user's computer |
||||
system, and (b) will operate properly with a modified version |
||||
of the Library that is interface-compatible with the Linked |
||||
Version. |
||||
|
||||
e) Provide Installation Information, but only if you would otherwise |
||||
be required to provide such information under section 6 of the |
||||
GNU GPL, and only to the extent that such information is |
||||
necessary to install and execute a modified version of the |
||||
Combined Work produced by recombining or relinking the |
||||
Application with a modified version of the Linked Version. (If |
||||
you use option 4d0, the Installation Information must accompany |
||||
the Minimal Corresponding Source and Corresponding Application |
||||
Code. If you use option 4d1, you must provide the Installation |
||||
Information in the manner specified by section 6 of the GNU GPL |
||||
for conveying Corresponding Source.) |
||||
|
||||
5. Combined Libraries. |
||||
|
||||
You may place library facilities that are a work based on the |
||||
Library side by side in a single library together with other library |
||||
facilities that are not Applications and are not covered by this |
||||
License, and convey such a combined library under terms of your |
||||
choice, if you do both of the following: |
||||
|
||||
a) Accompany the combined library with a copy of the same work based |
||||
on the Library, uncombined with any other library facilities, |
||||
conveyed under the terms of this License. |
||||
|
||||
b) Give prominent notice with the combined library that part of it |
||||
is a work based on the Library, and explaining where to find the |
||||
accompanying uncombined form of the same work. |
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License. |
||||
|
||||
The Free Software Foundation may publish revised and/or new versions |
||||
of the GNU Lesser General Public License from time to time. Such new |
||||
versions will be similar in spirit to the present version, but may |
||||
differ in detail to address new problems or concerns. |
||||
|
||||
Each version is given a distinguishing version number. If the |
||||
Library as you received it specifies that a certain numbered version |
||||
of the GNU Lesser General Public License "or any later version" |
||||
applies to it, you have the option of following the terms and |
||||
conditions either of that published version or of any later version |
||||
published by the Free Software Foundation. If the Library as you |
||||
received it does not specify a version number of the GNU Lesser |
||||
General Public License, you may choose any version of the GNU Lesser |
||||
General Public License ever published by the Free Software Foundation. |
||||
|
||||
If the Library as you received it specifies that a proxy can decide |
||||
whether future versions of the GNU Lesser General Public License shall |
||||
apply, that proxy's public statement of acceptance of any version is |
||||
permanent authorization for you to choose that version for the |
||||
Library. |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
libaudec |
||||
======== |
||||
|
||||
# Overview |
||||
libaudec (lib audio decoder) is a wrapper library over |
||||
ffmpeg, sndfile and libsamplerate for reading and |
||||
resampling audio files, based on Robin Gareus' `audio_decoder` |
||||
code (https://github.com/x42/silan/tree/master/audio_decoder) |
||||
|
||||
# License |
||||
libaudec is released under the GNU LGPLv3+. See the |
||||
file COPYING for more details. Some files, where |
||||
specified, are licensed under different licenses. |
||||
|
||||
---- |
||||
Copyright (C) 2019 Alexandros Theodotou |
||||
|
||||
Copying and distribution of this file, with or without modification, |
||||
are permitted in any medium without royalty provided the copyright |
||||
notice and this notice are preserved. This file is offered as-is, |
||||
without any warranty. |
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
@brief audio-decoder - wrapper around libsndfile and libav* |
||||
@file ad.h |
||||
@author Robin Gareus <robin@gareus.org> |
||||
|
||||
Copyright (C) 2011-2013 Robin Gareus <robin@gareus.org> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser Public License as published by |
||||
the Free Software Foundation; either version 2.1, or (at your option) |
||||
any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
|
||||
*/ |
||||
#ifndef __AD_H__ |
||||
#define __AD_H__ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif /* __cplusplus */ |
||||
|
||||
#include <unistd.h> |
||||
#include <stdint.h> |
||||
|
||||
struct adinfo { |
||||
unsigned int sample_rate; |
||||
unsigned int channels; |
||||
int64_t length; //milliseconds
|
||||
int64_t frames; //total number of frames (eg a frame for 16bit stereo is 4 bytes).
|
||||
int bit_rate; |
||||
int bit_depth; |
||||
char * meta_data; |
||||
}; |
||||
|
||||
/* global init function - register codecs */ |
||||
void ad_init(); |
||||
|
||||
/* --- public API --- */ |
||||
|
||||
/** open an audio file
|
||||
* @param fn file-name |
||||
* @param nfo pointer to a adinfo struct which will hold information about the file. |
||||
* @return NULL on error, a pointer to an opaque soundfile-decoder object on success. |
||||
*/ |
||||
void * ad_open (const char *fn, struct adinfo *nfo); |
||||
|
||||
/** close an audio file and release decoder structures
|
||||
* @param sf decoder handle |
||||
* @return 0 on succees, -1 if sf was invalid or not open (return value can usually be ignored) |
||||
*/ |
||||
int ad_close (void *sf); |
||||
|
||||
/** seel to a given position in the file
|
||||
* @param sf decoder handle |
||||
* @param pos frame position to seek to in frames (1 frame = number-of-channel samples) from the start of the file. |
||||
* @return the current position in frames (multi-channel samples) from the start of the file. On error this function returns -1. |
||||
*/ |
||||
int64_t ad_seek (void *sf, int64_t pos); |
||||
|
||||
/** decode audio data chunk to raw interleaved channel floating point data
|
||||
* |
||||
* @param sf decoder handle |
||||
* @param out place to store data -- must be large enough to hold (sizeof(float) * len) bytes. |
||||
* @param len number of samples (!) to read (should be a multiple of nfo->channels). |
||||
* @return the number of read samples. |
||||
*/ |
||||
ssize_t ad_read (void *sf, float* out, size_t len); |
||||
|
||||
/** re-read the file information and meta-data.
|
||||
* |
||||
* this is not neccesary in general \ref ad_open includes an inplicit call |
||||
* but meta-data may change in live-stream in which case en explicit call to |
||||
* ad_into is needed to update the inforation |
||||
* |
||||
* @param fn file-name |
||||
* @param nfo pointer to a adinfo struct which will hold information about the file. |
||||
* @return 0 on succees, -1 if sf was invalid or not open |
||||
*/ |
||||
int ad_info (void *sf, struct adinfo *nfo); |
||||
|
||||
/** zero initialize the information struct. * (does not free nfo->meta_data)
|
||||
* @param nfo pointer to a adinfo struct |
||||
*/ |
||||
void ad_clear_nfo (struct adinfo *nfo); |
||||
|
||||
/** free possibly allocated meta-data text
|
||||
* @param nfo pointer to a adinfo struct |
||||
*/ |
||||
void ad_free_nfo (struct adinfo *nfo); |
||||
|
||||
|
||||
/* --- helper functions --- */ |
||||
|
||||
/** read file info
|
||||
* combines ad_open() and ad_close() |
||||
*/ |
||||
int ad_finfo (const char *, struct adinfo *); |
||||
|
||||
/**
|
||||
* wrapper around \ref ad_read, downmixes all channels to mono |
||||
*/ |
||||
ssize_t ad_read_mono_dbl (void *, struct adinfo *, double*, size_t); |
||||
|
||||
/**
|
||||
* calls dbg() to print file info to stderr. |
||||
* |
||||
* @param dbglvl |
||||
* @param nfo |
||||
*/ |
||||
void ad_dump_nfo (int dbglvl, struct adinfo *nfo); |
||||
|
||||
/** set audio-decoder debug level -- all info is printed to stderr.
|
||||
* |
||||
* @param lvl debug-level threshold |
||||
* -1: absolutley silent |
||||
* 0: errors only |
||||
* 1: errors + info |
||||
* 2: + debug |
||||
* 3: + low-level-debug info |
||||
*/ |
||||
void ad_set_debuglevel(int lvl); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif /* __cplusplus */ |
||||
|
||||
#endif |
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
Copyright (C) 2011-2013 Robin Gareus <robin@gareus.org> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser Public License as published by |
||||
the Free Software Foundation; either version 2.1, or (at your option) |
||||
any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
|
||||
*/ |
||||
#ifndef __AD_PLUGIN_H__ |
||||
#define __AD_PLUGIN_H__ |
||||
#include <stdint.h> |
||||
#include "ad.h" |
||||
|
||||
#define dbg(A, B, ...) ad_debug_printf(__func__, A, B, ##__VA_ARGS__) |
||||
|
||||
#ifndef __PRI64_PREFIX |
||||
#if (defined __X86_64__ || defined __LP64__) |
||||
# define __PRI64_PREFIX "l" |
||||
#else |
||||
# define __PRI64_PREFIX "ll" |
||||
#endif |
||||
#endif |
||||
|
||||
#ifndef PRIu64 |
||||
# define PRIu64 __PRI64_PREFIX "u" |
||||
#endif |
||||
#ifndef PRIi64 |
||||
# define PRIi64 __PRI64_PREFIX "i" |
||||
#endif |
||||
|
||||
extern int ad_debug_level; |
||||
|
||||
void ad_debug_printf(const char* func, int level, const char* format, ...); |
||||
|
||||
typedef struct { |
||||
int (*eval)(const char *); |
||||
void * (*open)(const char *, struct adinfo *); |
||||
int (*close)(void *); |
||||
int (*info)(void *, struct adinfo *); |
||||
int64_t (*seek)(void *, int64_t); |
||||
ssize_t (*read)(void *, float *, size_t); |
||||
} ad_plugin; |
||||
|
||||
int ad_eval_null(const char *); |
||||
void * ad_open_null(const char *, struct adinfo *); |
||||
int ad_close_null(void *); |
||||
int ad_info_null(void *, struct adinfo *); |
||||
int64_t ad_seek_null(void *, int64_t); |
||||
ssize_t ad_read_null(void *, float*, size_t); |
||||
|
||||
/* hardcoded backends */ |
||||
const ad_plugin * adp_get_sndfile(); |
||||
const ad_plugin * adp_get_ffmpeg(); |
||||
#endif |
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/* ffmpeg compatibility wrappers
|
||||
* |
||||
* Copyright 2012,2013 Robin Gareus <robin@gareus.org> |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* 1. Redistributions of source code must retain the above copyright notice, this |
||||
* list of conditions and the following disclaimer. |
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, |
||||
* this list of conditions and the following disclaimer in the documentation |
||||
* and/or other materials provided with the distribution. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
#ifndef FFCOMPAT_H |
||||
#define FFCOMPAT_H |
||||
|
||||
#include <libavcodec/avcodec.h> |
||||
#include <libavformat/avformat.h> |
||||
#include <libavutil/avutil.h> |
||||
|
||||
#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE |
||||
#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 |
||||
#endif |
||||
|
||||
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(50, 0, 0) |
||||
#define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO |
||||
#endif |
||||
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0) |
||||
static inline int avformat_open_input(AVFormatContext **ps, const char *filename, void *fmt, void **options) |
||||
{ |
||||
return av_open_input_file(ps, filename, NULL, 0, NULL); |
||||
} |
||||
#endif /* avformat < 53.2.0 */ |
||||
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 5, 0) |
||||
static inline AVCodecContext * |
||||
avcodec_alloc_context3(AVCodec *codec __attribute__((unused))) |
||||
{ |
||||
return avcodec_alloc_context(); |
||||
} |
||||
|
||||
static inline AVStream * |
||||
avformat_new_stream(AVFormatContext *s, AVCodec *c) { |
||||
return av_new_stream(s,0); |
||||
} |
||||
|
||||
static inline int |
||||
avcodec_get_context_defaults3(AVCodecContext *s, AVCodec *codec) |
||||
{ |
||||
avcodec_get_context_defaults(s); |
||||
return 0; |
||||
} |
||||
|
||||
#endif /* < 53.5.0 */ |
||||
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 5, 6) |
||||
static inline int |
||||
avcodec_open2(AVCodecContext *avctx, AVCodec *codec, void **options __attribute__((unused))) |
||||
{ |
||||
return avcodec_open(avctx, codec); |
||||
} |
||||
#endif /* <= 53.5.6 */ |
||||
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 5, 0) |
||||
static inline int |
||||
avformat_find_stream_info(AVFormatContext *ic, void **options) |
||||
{ |
||||
return av_find_stream_info(ic); |
||||
} |
||||
|
||||
static inline void |
||||
avformat_close_input(AVFormatContext **s) |
||||
{ |
||||
av_close_input_file(*s); |
||||
} |
||||
|
||||
#endif /* < 53.5.0 */ |
||||
|
||||
#endif /* FFCOMPAT_H */ |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
# Copyright (C) 2019 Alexandros Theodotou <alex at zrythm dot org> |
||||
# |
||||
# This file is part of libaudec |
||||
# |
||||
# libaudec is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU Lesser General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# libaudec is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License |
||||
# along with libaudec. If not, see <https://www.gnu.org/licenses/>. |
||||
|
||||
install_headers('ad.h', subdir : 'audec') |
@ -0,0 +1,148 @@
@@ -0,0 +1,148 @@
|
||||
# Copyright (C) 2019 Alexandros Theodotou <alex at zrythm dot org> |
||||
# |
||||
# This file is part of libaudec |
||||
# |
||||
# libaudec is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU Lesser General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# libaudec is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License |
||||
# along with libaudec. If not, see <https://www.gnu.org/licenses/>. |
||||
|
||||
project ( |
||||
'libaudec', ['c'], |
||||
version: '0.1.0', |
||||
license: 'LGPLv3+', |
||||
meson_version: '>= 0.46.0', |
||||
default_options: [ |
||||
'warning_level=1', |
||||
'buildtype=debug', |
||||
'c_std=gnu11', |
||||
], |
||||
) |
||||
|
||||
srcs = [] |
||||
subdir('inc') |
||||
subdir('src') |
||||
|
||||
cc = meson.get_compiler ('c') |
||||
prefix = get_option ('prefix') |
||||
includedir = join_paths ( |
||||
prefix, get_option('includedir')) |
||||
libdir = join_paths (prefix, get_option ('libdir')) |
||||
|
||||
cdata = configuration_data () |
||||
cdata.set_quoted ( |
||||
'PACKAGE_VERSION', |
||||
'@VCS_TAG@') |
||||
cdata.set_quoted ( |
||||
'COMPILER', |
||||
meson.get_compiler('c').get_id()) |
||||
cdata.set_quoted ( |
||||
'PREFIX', prefix) |
||||
cdata.set_quoted ( |
||||
'COMPILER_VERSION', |
||||
meson.get_compiler('c').version()) |
||||
cdata.set_quoted ( |
||||
'CONFIGURE_LIBDIR', |
||||
libdir) |
||||
|
||||
# detect OS |
||||
os_darwin = false |
||||
os_linux = false |
||||
os_freebsd = false |
||||
if host_machine.system() == 'darwin' |
||||
os_darwin = true |
||||
elif host_machine.system() == 'linux' |
||||
os_linux = true |
||||
elif host_machine.system() == 'freebsd' |
||||
os_freebsd = true |
||||
endif |
||||
|
||||
inc = [ |
||||
include_directories ('.'), |
||||
include_directories ('inc'), |
||||
] |
||||
|
||||
# Compiler flags |
||||
audec_cflags = [ |
||||
'-Wall', |
||||
'-Wformat=2', |
||||
'-Wno-missing-field-initializers', |
||||
'-Wno-unused-parameter', |
||||
'-Wno-sequence-point', |
||||
'-Wignored-qualifiers', |
||||
'-Wno-cast-function-type', |
||||
] |
||||
audec_cflags = cc.get_supported_arguments ( |
||||
audec_cflags) |
||||
|
||||
audec_deps = [] |
||||
if (get_option('enable_ffmpeg')) |
||||
audec_deps += dependency('libavcodec') |
||||
audec_deps += dependency('libavformat') |
||||
audec_deps += dependency('libavutil') |
||||
cdata.set('HAVE_FFMPEG', 1) |
||||
endif |
||||
|
||||
audec_deps += [ |
||||
dependency('sndfile', version: '>=1.0.25'), |
||||
dependency('samplerate', version: '>=0.1.8'), |
||||
] |
||||
|
||||
# create config.h and add to deps |
||||
tmp_h = configure_file ( |
||||
output: 'tmp.h', |
||||
configuration: cdata, |
||||
) |
||||
config_h_vcs = vcs_tag ( |
||||
input: tmp_h, |
||||
output: 'config.h', |
||||
) |
||||
config_h_dep = declare_dependency ( |
||||
sources: config_h_vcs, |
||||
) |
||||
audec_deps += config_h_dep |
||||
|
||||
audec = static_library ( |
||||
'audec', |
||||
sources: srcs, |
||||
dependencies: [ |
||||
audec_deps, |
||||
], |
||||
include_directories: inc, |
||||
install: true, |
||||
) |
||||
|
||||
pkg_mod = import('pkgconfig') |
||||
pkg_mod.generate( |
||||
libraries: audec, |
||||
version: '0.1', |
||||
name: 'audec', |
||||
filebase: 'audec', |
||||
description: 'A library to read and resample audio files.', |
||||
) |
||||
|
||||
summary = [ |
||||
'', |
||||
'------', |
||||
'libaudec @0@'.format(meson.project_version()), |
||||
'', |
||||
' Build type: @0@'.format( |
||||
get_option('buildtype')), |
||||
' Optional libraries:', |
||||
' ffmpeg: @0@'.format(get_option('enable_ffmpeg')), |
||||
' Directories:', |
||||
' prefix: @0@'.format(prefix), |
||||
' includedir: @0@'.format(includedir), |
||||
' libdir: @0@'.format(libdir), |
||||
'------', |
||||
'' |
||||
] |
||||
message('\n'.join(summary)) |
@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2019 Alexandros Theodotou <alex at zrythm dot org> |
||||
# |
||||
# This file is part of libaudec |
||||
# |
||||
# libaudec is free software: you can redistribute it and/or modify |
||||
# it under the terms of the GNU Lesser General Public License as published by |
||||
# the Free Software Foundation, either version 3 of the License, or |
||||
# (at your option) any later version. |
||||
# |
||||
# libaudec is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU Lesser General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU Lesser General Public License |
||||
# along with libaudec. If not, see <https://www.gnu.org/licenses/>. |
||||
|
||||
option ( |
||||
'enable_ffmpeg', |
||||
type: 'boolean', |
||||
value: false, |
||||
description: 'Compile with ffmpeg (for MP3 support)') |
@ -0,0 +1,389 @@
@@ -0,0 +1,389 @@
|
||||
/**
|
||||
Copyright (C) 2011-2013 Robin Gareus <robin@gareus.org> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser Public License as published by |
||||
the Free Software Foundation; either version 2.1, or (at your option) |
||||
any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
|
||||
*/ |
||||
|
||||
#include "config.h" |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <math.h> |
||||
|
||||
#include "ad_plugin.h" |
||||
|
||||
#ifdef HAVE_FFMPEG |
||||
|
||||
#include "ffcompat.h" |
||||
|
||||
#ifndef MIN |
||||
#define MIN(a,b) ( ( (a) < (b) )? (a) : (b) ) |
||||
#endif |
||||
|
||||
typedef struct { |
||||
AVFormatContext* formatContext; |
||||
AVCodecContext* codecContext; |
||||
AVCodec* codec; |
||||
AVPacket packet; |
||||
int audioStream; |
||||
int pkt_len; |
||||
uint8_t* pkt_ptr; |
||||
|
||||
int16_t m_tmpBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE]; |
||||
int16_t* m_tmpBufferStart; |
||||
unsigned long m_tmpBufferLen; |
||||
|
||||
int64_t decoder_clock; |
||||
int64_t output_clock; |
||||
int64_t seek_frame; |
||||
unsigned int samplerate; |
||||
unsigned int channels; |
||||
int64_t length; |
||||
} ffmpeg_audio_decoder; |
||||
|
||||
|
||||
static int ad_info_ffmpeg(void *sf, struct adinfo *nfo) { |
||||
ffmpeg_audio_decoder *priv = (ffmpeg_audio_decoder*) sf; |
||||
if (!priv) return -1; |
||||
if (nfo) { |
||||
nfo->sample_rate = priv->samplerate; |
||||
nfo->channels = priv->channels; |
||||
nfo->frames = priv->length; |
||||
if (nfo->sample_rate==0) return -1; |
||||
nfo->length = (nfo->frames * 1000) / nfo->sample_rate; |
||||
nfo->bit_rate = priv->formatContext->bit_rate; |
||||
nfo->bit_depth = 0; |
||||
nfo->meta_data = NULL; |
||||
|
||||
#ifdef WITH_GTK // XXX replace g_* functions with POSIX equiv
|
||||
AVDictionaryEntry *tag = NULL; |
||||
// Tags in container
|
||||
while ((tag = av_dict_get(priv->formatContext->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { |
||||
dbg(2, "FTAG: %s=%s", tag->key, tag->value); |
||||
char * tmp = g_strdup_printf("%s%s<i>%s</i>:%s", nfo->meta_data?nfo->meta_data:"",nfo->meta_data?"\n":"", tag->key, tag->value); |
||||
if (nfo->meta_data) g_free(nfo->meta_data); |
||||
nfo->meta_data = tmp; |
||||
} |
||||
// Tags in stream
|
||||
tag=NULL; |
||||
AVStream *stream = priv->formatContext->streams[priv->audioStream]; |
||||
while ((tag = av_dict_get(stream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { |
||||
dbg(2, "STAG: %s=%s", tag->key, tag->value); |
||||
char * tmp = g_strdup_printf("%s%s<i>%s</i>:%s", nfo->meta_data?nfo->meta_data:"",nfo->meta_data?"\n":"", tag->key, tag->value); |
||||
if (nfo->meta_data) g_free(nfo->meta_data); |
||||
nfo->meta_data = tmp; |
||||
} |
||||
#endif |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static void *ad_open_ffmpeg(const char *fn, struct adinfo *nfo) { |
||||
ffmpeg_audio_decoder *priv = (ffmpeg_audio_decoder*) calloc(1, sizeof(ffmpeg_audio_decoder)); |
||||
|
||||
priv->m_tmpBufferStart=NULL; |
||||
priv->m_tmpBufferLen=0; |
||||
priv->decoder_clock=priv->output_clock=priv->seek_frame=0; |
||||
priv->packet.size=0; priv->packet.data=NULL; |
||||
|
||||
if (avformat_open_input(&priv->formatContext, fn, NULL, NULL) <0) { |
||||
dbg(0, "ffmpeg is unable to open file '%s'.", fn); |
||||
free(priv); return(NULL); |
||||
} |
||||
|
||||
if (avformat_find_stream_info(priv->formatContext, NULL) < 0) { |
||||
avformat_close_input(&priv->formatContext); |
||||
dbg(0, "av_find_stream_info failed" ); |
||||
free(priv); return(NULL); |
||||
} |
||||
|
||||
priv->audioStream = -1; |
||||
unsigned int i; |
||||
for (i=0; i<priv->formatContext->nb_streams; i++) { |
||||
if (priv->formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { |
||||
priv->audioStream = i; |
||||
break; |
||||
} |
||||
} |
||||
if (priv->audioStream == -1) { |
||||
dbg(0, "No Audio Stream found in file"); |
||||
avformat_close_input(&priv->formatContext); |
||||
free(priv); return(NULL); |
||||
} |
||||
|
||||
priv->codecContext = priv->formatContext->streams[priv->audioStream]->codec; |
||||
priv->codec = avcodec_find_decoder(priv->codecContext->codec_id); |
||||
|
||||
if (priv->codec == NULL) { |
||||
avformat_close_input(&priv->formatContext); |
||||
dbg(0, "Codec not supported by ffmpeg"); |
||||
free(priv); return(NULL); |
||||
} |
||||
if (avcodec_open2(priv->codecContext, priv->codec, NULL) < 0) { |
||||
dbg(0, "avcodec_open failed" ); |
||||
free(priv); return(NULL); |
||||
} |
||||
|
||||
dbg(2, "ffmpeg - audio tics: %i/%i [sec]",priv->formatContext->streams[priv->audioStream]->time_base.num,priv->formatContext->streams[priv->audioStream]->time_base.den); |
||||
|
||||
int64_t len = priv->formatContext->duration - priv->formatContext->start_time; |
||||
|
||||
priv->formatContext->flags|=AVFMT_FLAG_GENPTS; |
||||
priv->formatContext->flags|=AVFMT_FLAG_IGNIDX; |
||||
|
||||
priv->samplerate = priv->codecContext->sample_rate; |
||||
priv->channels = priv->codecContext->channels ; |
||||
priv->length = (int64_t)( len * priv->samplerate / AV_TIME_BASE ); |
||||
|
||||
if (ad_info_ffmpeg((void*)priv, nfo)) { |
||||
dbg(0, "invalid file info (sample-rate==0)"); |
||||
free(priv); return(NULL); |
||||
} |
||||
|
||||
dbg(1, "ffmpeg - %s", fn); |
||||
if (nfo) |
||||
dbg(1, "ffmpeg - sr:%i c:%i d:%"PRIi64" f:%"PRIi64, nfo->sample_rate, nfo->channels, nfo->length, nfo->frames); |
||||
|
||||
return (void*) priv; |
||||
} |
||||
|
||||
static int ad_close_ffmpeg(void *sf) { |
||||
ffmpeg_audio_decoder *priv = (ffmpeg_audio_decoder*) sf; |
||||
if (!priv) return -1; |
||||
avcodec_close(priv->codecContext); |
||||
avformat_close_input(&priv->formatContext); |
||||
free(priv); |
||||
return 0; |
||||
} |
||||
|
||||
static void int16_to_float(int16_t *in, float *out, int num_channels, int num_samples, int out_offset) { |
||||
int i,ii; |
||||
for (i=0;i<num_samples;i++) { |
||||
for (ii=0;ii<num_channels;ii++) { |
||||
out[(i+out_offset)*num_channels+ii]= (float) in[i*num_channels+ii]/ 32768.0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static ssize_t ad_read_ffmpeg(void *sf, float* d, size_t len) { |
||||
ffmpeg_audio_decoder *priv = (ffmpeg_audio_decoder*) sf; |
||||
if (!priv) return -1; |
||||
size_t frames = len / priv->channels; |
||||
|
||||
size_t written = 0; |
||||
ssize_t ret = 0; |
||||
while (ret >= 0 && written < frames) { |
||||
dbg(3,"loop: %i/%i (bl:%lu)",written, frames, priv->m_tmpBufferLen ); |
||||
if (priv->seek_frame == 0 && priv->m_tmpBufferLen > 0 ) { |
||||
int s = MIN(priv->m_tmpBufferLen / priv->channels, frames - written ); |
||||
int16_to_float(priv->m_tmpBufferStart, d, priv->channels, s , written); |
||||
written += s; |
||||
priv->output_clock+=s; |
||||
s = s * priv->channels; |
||||
priv->m_tmpBufferStart += s; |
||||
priv->m_tmpBufferLen -= s; |
||||
ret = 0; |
||||
} else { |
||||
priv->m_tmpBufferStart = priv->m_tmpBuffer; |
||||
priv->m_tmpBufferLen = 0; |
||||
|
||||
if (!priv->pkt_ptr || priv->pkt_len <1 ) { |
||||
if (priv->packet.data) av_free_packet(&priv->packet); |
||||
ret = av_read_frame(priv->formatContext, &priv->packet); |
||||
if (ret<0) { dbg(1, "reached end of file."); break; } |
||||
priv->pkt_len = priv->packet.size; |
||||
priv->pkt_ptr = priv->packet.data; |
||||
} |
||||
|
||||
if (priv->packet.stream_index != priv->audioStream) { |
||||
priv->pkt_ptr = NULL; |
||||
continue; |
||||
} |
||||
|
||||
/* decode all chunks in packet */ |
||||
int data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; |
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 0, 0) |
||||
// TODO use av_frame_alloc() and av_frame_free() with newer ffmpeg
|
||||
AVFrame avf; |
||||
memset(&avf, 0, sizeof(AVFrame)); |
||||
int got_frame = 0; |
||||
ret = avcodec_decode_audio4(priv->codecContext, &avf, &got_frame, &priv->packet); |
||||
if (ret >= 0 && got_frame) { |
||||
int ch, plane_size; |
||||
const int planar = av_sample_fmt_is_planar(priv->codecContext->sample_fmt); |
||||
data_size = av_samples_get_buffer_size(&plane_size, priv->codecContext->channels, avf.nb_samples, priv->codecContext->sample_fmt, 1); |
||||
if (data_size <= AVCODEC_MAX_AUDIO_FRAME_SIZE) { |
||||
memcpy(priv->m_tmpBuffer, avf.extended_data[0], plane_size); |
||||
if (planar && priv->codecContext->channels > 1) { |
||||
uint8_t *out = ((uint8_t *)priv->m_tmpBuffer) + plane_size; |
||||
for (ch = 1; ch < priv->codecContext->channels; ch++) { |
||||
memcpy(out, avf.extended_data[ch], plane_size); |
||||
out += plane_size; |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
ret = -1; |
||||
} |
||||
#elif LIBAVUTIL_VERSION_INT > AV_VERSION_INT(49, 15, 0) && LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52, 20, 1) // ??
|
||||
// this was deprecated in LIBAVCODEC_VERSION_MAJOR 53
|
||||
ret = avcodec_decode_audio3(priv->codecContext, |
||||
priv->m_tmpBuffer, &data_size, &priv->packet); |
||||
#else |
||||
int len = priv->packet.size; |
||||
uint8_t *ptr = priv->packet.data; |
||||
ret = avcodec_decode_audio2(priv->codecContext, |
||||
priv->m_tmpBuffer, &data_size, ptr, len); |
||||
#endif |
||||
|
||||
if (ret < 0 || ret > priv->pkt_len) { |
||||
#if 0 |
||||
dbg(0, "audio decode error"); |
||||
return -1; |
||||
#endif |
||||
priv->pkt_len=0; |
||||
ret=0; |
||||
continue; |
||||
} |
||||
|
||||
priv->pkt_len -= ret; priv->pkt_ptr += ret; |
||||
|
||||
/* sample exact alignment */ |
||||
if (priv->packet.pts != AV_NOPTS_VALUE) { |
||||
priv->decoder_clock = priv->samplerate * av_q2d(priv->formatContext->streams[priv->audioStream]->time_base) * priv->packet.pts; |
||||
} else { |
||||
dbg(0, "!!! NO PTS timestamp in file"); |
||||
priv->decoder_clock += (data_size>>1) / priv->channels; |
||||
} |
||||
|
||||
if (data_size>0) { |
||||
priv->m_tmpBufferLen+= (data_size>>1); // 2 bytes per sample
|
||||
} |
||||
|
||||
/* align buffer after seek. */ |
||||
if (priv->seek_frame > 0) { |
||||
const int diff = priv->output_clock-priv->decoder_clock; |
||||
if (diff<0) { |
||||
/* seek ended up past the wanted sample */ |
||||
dbg(0, " !!! Audio seek failed."); |
||||
return -1; |
||||
} else if (priv->m_tmpBufferLen < (diff*priv->channels)) { |
||||
/* wanted sample not in current buffer - keep going */ |
||||
dbg(2, " !!! seeked sample was not in decoded buffer. frames-to-go: %li", diff); |
||||
priv->m_tmpBufferLen = 0; |
||||
} else if (diff!=0 && data_size > 0) { |
||||
/* wanted sample is in current buffer but not at the beginnning */ |
||||
dbg(2, " !!! sync buffer to seek. (diff:%i)", diff); |
||||
priv->m_tmpBufferStart+= diff*priv->codecContext->channels; |
||||
priv->m_tmpBufferLen -= diff*priv->codecContext->channels; |
||||
#if 1 |
||||
memmove(priv->m_tmpBuffer, priv->m_tmpBufferStart, priv->m_tmpBufferLen); |
||||
priv->m_tmpBufferStart = priv->m_tmpBuffer; |
||||
#endif |
||||
priv->seek_frame=0; |
||||
priv->decoder_clock += diff; |
||||
} else if (data_size > 0) { |
||||
dbg(2, "Audio exact sync-seek (%"PRIi64" == %"PRIi64")", priv->decoder_clock, priv->seek_frame); |
||||
priv->seek_frame=0; |
||||
} else { |
||||
dbg(0, "Error: no audio data in packet"); |
||||
} |
||||
} |
||||
//dbg(0, "PTS: decoder:%"PRIi64". - want: %"PRIi64, priv->decoder_clock, priv->output_clock);
|
||||
//dbg(0, "CLK: frame: %"PRIi64" T:%.3fs",priv->decoder_clock, (float) priv->decoder_clock/priv->samplerate);
|
||||
} |
||||
} |
||||
if (written!=frames) { |
||||
dbg(2, "short-read"); |
||||
} |
||||
return written * priv->channels; |
||||
} |
||||
|
||||
static int64_t ad_seek_ffmpeg(void *sf, int64_t pos) { |
||||
ffmpeg_audio_decoder *priv = (ffmpeg_audio_decoder*) sf; |
||||
if (!sf) return -1; |
||||
if (pos == priv->output_clock) return pos; |
||||
|
||||
/* flush internal buffer */ |
||||
priv->m_tmpBufferLen = 0; |
||||
priv->seek_frame = pos; |
||||
priv->output_clock = pos; |
||||
priv->pkt_len = 0; priv->pkt_ptr = NULL; |
||||
priv->decoder_clock = 0; |
||||
|
||||
#if 0 |
||||
/* TODO seek at least 1 packet before target.
|
||||
* for mpeg compressed files, the |
||||
* output may depend on past frames! */ |
||||
if (pos > 8192) pos -= 8192; |
||||
else pos = 0; |
||||
#endif |
||||
|
||||
const int64_t timestamp = pos / av_q2d(priv->formatContext->streams[priv->audioStream]->time_base) / priv->samplerate; |
||||
dbg(2, "seek frame:%"PRIi64" - idx:%"PRIi64, pos, timestamp); |
||||
|
||||
av_seek_frame(priv->formatContext, priv->audioStream, timestamp, AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD); |
||||
avcodec_flush_buffers(priv->codecContext); |
||||
return pos; |
||||
} |
||||
|
||||
static int ad_eval_ffmpeg(const char *f) { |
||||
char *ext = strrchr(f, '.'); |
||||
if (!ext) return 10; |
||||
// libavformat.. guess_format..
|
||||
return 40; |
||||
} |
||||
#endif |
||||
|
||||
static const ad_plugin ad_ffmpeg = { |
||||
#ifdef HAVE_FFMPEG |
||||
&ad_eval_ffmpeg, |
||||
&ad_open_ffmpeg, |
||||
&ad_close_ffmpeg, |
||||
&ad_info_ffmpeg, |
||||
&ad_seek_ffmpeg, |
||||
&ad_read_ffmpeg |
||||
#else |
||||
&ad_eval_null, |
||||
&ad_open_null, |
||||
&ad_close_null, |
||||
&ad_info_null, |
||||
&ad_seek_null, |
||||
&ad_read_null |
||||
#endif |
||||
}; |
||||
|
||||
/* dlopen handler */ |
||||
const ad_plugin * adp_get_ffmpeg() { |
||||
#ifdef HAVE_FFMPEG |
||||
static int ffinit = 0; |
||||
if (!ffinit) { |
||||
ffinit=1; |
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 5, 0) |
||||
avcodec_init(); |
||||
#endif |
||||
av_register_all(); |
||||
avcodec_register_all(); |
||||
if(ad_debug_level <= 1) |
||||
av_log_set_level(AV_LOG_QUIET); |
||||
else |
||||
av_log_set_level(AV_LOG_VERBOSE); |
||||
} |
||||
#endif |
||||
return &ad_ffmpeg; |
||||
} |
@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
Copyright (C) 2011-2013 Robin Gareus <robin@gareus.org> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser Public License as published by |
||||
the Free Software Foundation; either version 2.1, or (at your option) |
||||
any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
|
||||
*/ |
||||
#include "config.h" |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdarg.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <math.h> |
||||
|
||||
#include "ad_plugin.h" |
||||
|
||||
int ad_debug_level = 0; |
||||
|
||||
#define UNUSED(x) (void)(x) |
||||
|
||||
int ad_eval_null(const char *f) { UNUSED(f); return -1; } |
||||
void * ad_open_null(const char *f, struct adinfo *n) { UNUSED(f); UNUSED(n); return NULL; } |
||||
int ad_close_null(void *x) { UNUSED(x); return -1; } |
||||
int ad_info_null(void *x, struct adinfo *n) { UNUSED(x); UNUSED(n); return -1; } |
||||
int64_t ad_seek_null(void *x, int64_t p) { UNUSED(x); UNUSED(p); return -1; } |
||||
ssize_t ad_read_null(void *x, float*d, size_t s) { UNUSED(x); UNUSED(d); UNUSED(s); return -1;} |
||||
|
||||
typedef struct { |
||||
ad_plugin const *b; ///< decoder back-end
|
||||
void *d; ///< backend data
|
||||
} adecoder; |
||||
|
||||
/* samplecat api */ |
||||
|
||||
void ad_init() { /* global init */ } |
||||
|
||||
static ad_plugin const * choose_backend(const char *fn) { |
||||
int max, val; |
||||
ad_plugin const *b=NULL; |
||||
max=0; |
||||
|
||||
val=adp_get_sndfile()->eval(fn); |
||||
if (val>max) {max=val; b=adp_get_sndfile();} |
||||
|
||||
val=adp_get_ffmpeg()->eval(fn); |
||||
if (val>max) {max=val; b=adp_get_ffmpeg();} |
||||
|
||||
return b; |
||||
} |
||||
|
||||
void *ad_open(const char *fn, struct adinfo *nfo) { |
||||
adecoder *d = (adecoder*) calloc(1, sizeof(adecoder)); |
||||
ad_clear_nfo(nfo); |
||||
|
||||
d->b = choose_backend(fn); |
||||
if (!d->b) { |
||||
dbg(0, "fatal: no decoder backend available"); |
||||
free(d); |
||||
return NULL; |
||||
} |
||||
d->d = d->b->open(fn, nfo); |
||||
if (!d->d) { |
||||
free(d); |
||||
return NULL; |
||||
} |
||||
return (void*)d; |
||||
} |
||||
|
||||
int ad_info(void *sf, struct adinfo *nfo) { |
||||
adecoder *d = (adecoder*) sf; |
||||
if (!d) return -1; |
||||
return d->b->info(d->d, nfo); |
||||
} |
||||
|
||||
int ad_close(void *sf) { |
||||
adecoder *d = (adecoder*) sf; |
||||
if (!d) return -1; |
||||
int rv = d->b->close(d->d); |
||||
free(d); |
||||
return rv; |
||||
} |
||||
|
||||
int64_t ad_seek(void *sf, int64_t pos) { |
||||
adecoder *d = (adecoder*) sf; |
||||
if (!d) return -1; |
||||
return d->b->seek(d->d, pos); |
||||
} |
||||
|
||||
ssize_t ad_read(void *sf, float* out, size_t len){ |
||||
adecoder *d = (adecoder*) sf; |
||||
if (!d) return -1; |
||||
return d->b->read(d->d, out, len); |
||||
} |
||||
|
||||
/*
|
||||
* side-effects: allocates buffer |
||||
*/ |
||||
ssize_t ad_read_mono_dbl(void *sf, struct adinfo *nfo, double* d, size_t len){ |
||||
unsigned int c,f; |
||||
unsigned int chn = nfo->channels; |
||||
if (len<1) return 0; |
||||
|
||||
static float *buf = NULL; |
||||
static size_t bufsiz = 0; |
||||
if (!buf || bufsiz != len*chn) { |
||||
bufsiz=len*chn; |
||||
buf = (float*) realloc((void*)buf, bufsiz * sizeof(float)); |
||||
} |
||||
|
||||
len = ad_read(sf, buf, bufsiz); |
||||
|
||||
for (f=0;f< (len/chn);f++) { |
||||
double val=0.0; |
||||
for (c=0;c<chn;c++) { |
||||
val+=buf[f*chn + c]; |
||||
} |
||||
d[f]= val/chn; |
||||
} |
||||
return len/chn; |
||||
} |
||||
|
||||
|
||||
int ad_finfo (const char *fn, struct adinfo *nfo) { |
||||
ad_clear_nfo(nfo); |
||||
void * sf = ad_open(fn, nfo); |
||||
return ad_close(sf)?1:0; |
||||
} |
||||
|
||||
void ad_clear_nfo(struct adinfo *nfo) { |
||||
memset(nfo, 0, sizeof(struct adinfo)); |
||||
} |
||||
|
||||
void ad_free_nfo(struct adinfo *nfo) { |
||||
if (nfo->meta_data) free(nfo->meta_data); |
||||
} |
||||
|
||||
void ad_dump_nfo(int dbglvl, struct adinfo *nfo) { |
||||
dbg(dbglvl, "sample_rate: %u", nfo->sample_rate); |
||||
dbg(dbglvl, "channels: %u", nfo->channels); |
||||
dbg(dbglvl, "length: %"PRIi64" ms", nfo->length); |
||||
dbg(dbglvl, "frames: %"PRIi64, nfo->frames); |
||||
dbg(dbglvl, "bit_rate: %d", nfo->bit_rate); |
||||
dbg(dbglvl, "bit_depth: %d", nfo->bit_depth); |
||||
dbg(dbglvl, "channels: %u", nfo->channels); |
||||
dbg(dbglvl, "meta-data: %s", nfo->meta_data?nfo->meta_data:"-"); |
||||
} |
||||
|
||||
void ad_debug_printf(const char* func, int level, const char* format, ...) { |
||||
va_list args; |
||||
|
||||
va_start(args, format); |
||||
if (level <= ad_debug_level) { |
||||
fprintf(stderr, "%s(): ", func); |
||||
vfprintf(stderr, format, args); |
||||
fprintf(stderr, "\n"); |
||||
} |
||||
va_end(args); |
||||
} |
||||
|
||||
void ad_set_debuglevel(int lvl) { |
||||
ad_debug_level = lvl; |
||||
if (ad_debug_level<-1) ad_debug_level=-1; |
||||
if (ad_debug_level>3) ad_debug_level=3; |
||||
} |
@ -0,0 +1,155 @@
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
Copyright (C) 2011-2013 Robin Gareus <robin@gareus.org> |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of the GNU Lesser Public License as published by |
||||
the Free Software Foundation; either version 2.1, or (at your option) |
||||
any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU Lesser Public License for more details. |
||||
|
||||
You should have received a copy of the GNU Lesser General Public |
||||
License along with this library; if not, write to the Free Software |
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
||||
|
||||
*/ |
||||
#include "config.h" |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <strings.h> |
||||
#include <unistd.h> |
||||
#include <math.h> |
||||
#include <sndfile.h> |
||||
|
||||
#include "ad_plugin.h" |
||||
|
||||
/* internal abstraction */ |
||||
|
||||
typedef struct { |
||||
SF_INFO sfinfo; |
||||
SNDFILE *sffile; |
||||
} sndfile_audio_decoder; |
||||
|
||||
static int parse_bit_depth(int format) { |
||||
/* see http://www.mega-nerd.com/libsndfile/api.html */ |
||||
switch (format&0x0f) { |
||||
case SF_FORMAT_PCM_S8: return 8; |
||||
case SF_FORMAT_PCM_16: return 16; /* Signed 16 bit data */ |
||||
case SF_FORMAT_PCM_24: return 24; /* Signed 24 bit data */ |
||||
case SF_FORMAT_PCM_32: return 32; /* Signed 32 bit data */ |
||||
case SF_FORMAT_PCM_U8: return 8; /* Unsigned 8 bit data (WAV and RAW only) */ |
||||
case SF_FORMAT_FLOAT : return 32; /* 32 bit float data */ |
||||
case SF_FORMAT_DOUBLE: return 64; /* 64 bit float data */ |
||||
default: break; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static int ad_info_sndfile(void *sf, struct adinfo *nfo) { |
||||
sndfile_audio_decoder *priv = (sndfile_audio_decoder*) sf; |
||||
if (!priv) return -1; |
||||
if (nfo) { |
||||
nfo->channels = priv->sfinfo.channels; |
||||
nfo->frames = priv->sfinfo.frames; |
||||
nfo->sample_rate = priv->sfinfo.samplerate; |
||||
nfo->length = priv->sfinfo.samplerate ? (priv->sfinfo.frames * 1000) / priv->sfinfo.samplerate : 0; |
||||
nfo->bit_depth = parse_bit_depth(priv->sfinfo.format); |
||||
nfo->bit_rate = nfo->bit_depth * nfo->channels * nfo->sample_rate; |
||||
nfo->meta_data = NULL; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static void *ad_open_sndfile(const char *fn, struct adinfo *nfo) { |
||||
sndfile_audio_decoder *priv = (sndfile_audio_decoder*) calloc(1, sizeof(sndfile_audio_decoder)); |
||||
priv->sfinfo.format=0; |
||||
if(!(priv->sffile = sf_open(fn, SFM_READ, &priv->sfinfo))){ |
||||
dbg(0, "unable to open file '%s'.", fn); |
||||
puts(sf_strerror(NULL)); |
||||
int e = sf_error(NULL); |
||||
dbg(0, "error=%i", e); |
||||
free(priv); |
||||
return NULL; |
||||
} |
||||
ad_info_sndfile(priv, nfo); |
||||
return (void*) priv; |
||||
} |
||||
|
||||
static int ad_close_sndfile(void *sf) { |
||||
sndfile_audio_decoder *priv = (sndfile_audio_decoder*) sf; |
||||
if (!priv) return -1; |
||||
if(!sf || sf_close(priv->sffile)) { |
||||
dbg(0, "fatal: bad file close.\n"); |
||||
return -1; |
||||
} |
||||
free(priv); |
||||
return 0; |
||||
} |
||||
|
||||
static int64_t ad_seek_sndfile(void *sf, int64_t pos) { |
||||
sndfile_audio_decoder *priv = (sndfile_audio_decoder*) sf; |
||||
if (!priv) return -1; |
||||
return sf_seek(priv->sffile, pos, SEEK_SET); |
||||
} |
||||
|
||||
static ssize_t ad_read_sndfile(void *sf, float* d, size_t len) { |
||||
sndfile_audio_decoder *priv = (sndfile_audio_decoder*) sf; |
||||
if (!priv) return -1; |
||||
return sf_read_float (priv->sffile, d, len); |
||||
} |
||||
|
||||
static int ad_eval_sndfile(const char *f) { |
||||
char *ext = strrchr(f, '.'); |
||||
if (strstr (f, "://")) return 0; |
||||
if (!ext) return 5; |
||||
/* see http://www.mega-nerd.com/libsndfile/ */ |
||||
if (!strcasecmp(ext, ".wav")) return 100; |
||||
if (!strcasecmp(ext, ".aiff")) return 100; |
||||
if (!strcasecmp(ext, ".aifc")) return 100; |
||||
if (!strcasecmp(ext, ".snd")) return 100; |
||||
if (!strcasecmp(ext, ".au")) return 100; |
||||
if (!strcasecmp(ext, ".paf")) return 100; |
||||
if (!strcasecmp(ext, ".iff")) return 100; |
||||
if (!strcasecmp(ext, ".svx")) return 100; |
||||
if (!strcasecmp(ext, ".sf")) return 100; |
||||
if (!strcasecmp(ext, ".vcc")) return 100; |
||||
if (!strcasecmp(ext, ".w64")) return 100; |
||||
if (!strcasecmp(ext, ".mat4")) return 100; |
||||
if (!strcasecmp(ext, ".mat5")) return 100; |
||||
if (!strcasecmp(ext, ".pvf5")) return 100; |
||||
if (!strcasecmp(ext, ".xi")) return 100; |
||||
if (!strcasecmp(ext, ".htk")) return 100; |
||||
if (!strcasecmp(ext, ".pvf")) return 100; |
||||
if (!strcasecmp(ext, ".sd2")) return 100; |
||||
// libsndfile >= 1.0.18
|
||||
if (!strcasecmp(ext, ".flac")) return 80; |
||||
if (!strcasecmp(ext, ".ogg")) return 80; |
||||
return 0; |
||||
} |
||||
|
||||
static const ad_plugin ad_sndfile = { |
||||
#if 1 |
||||
&ad_eval_sndfile, |
||||
&ad_open_sndfile, |
||||
&ad_close_sndfile, |
||||
&ad_info_sndfile, |
||||
&ad_seek_sndfile, |
||||
&ad_read_sndfile |
||||
#else |
||||
&ad_eval_null, |
||||
&ad_open_null, |
||||
&ad_close_null, |
||||
&ad_info_null, |
||||
&ad_seek_null, |
||||
&ad_read_null |
||||
#endif |
||||
}; |
||||
|
||||
/* dlopen handler */ |
||||
const ad_plugin * adp_get_sndfile() { |
||||
return &ad_sndfile; |
||||
} |