This adds back audio_decoder library as module as well. Likely breaks bridges build, to be handled soon.tags/1.9.8
| @@ -56,6 +56,7 @@ ALL_LIBS += $(MODULEDIR)/carla_engine_plugin.a | |||
| ALL_LIBS += $(MODULEDIR)/carla_plugin.a | |||
| ALL_LIBS += $(MODULEDIR)/jackbridge.a | |||
| ALL_LIBS += $(MODULEDIR)/native-plugins.a | |||
| ALL_LIBS += $(MODULEDIR)/audio_decoder.a | |||
| ALL_LIBS += $(MODULEDIR)/lilv.a | |||
| ALL_LIBS += $(MODULEDIR)/rtmempool.a | |||
| ALL_LIBS += $(MODULEDIR)/water.a | |||
| @@ -183,9 +183,11 @@ HAVE_ALSA = $(shell pkg-config --exists alsa && echo true) | |||
| HAVE_HYLIA = true | |||
| endif | |||
| HAVE_FFMPEG = $(shell pkg-config --exists libavcodec libavformat libavutil && echo true) | |||
| HAVE_FLUIDSYNTH = $(shell pkg-config --exists fluidsynth && echo true) | |||
| HAVE_LIBLO = $(shell pkg-config --exists liblo && echo true) | |||
| HAVE_LINUXSAMPLER = $(shell pkg-config --atleast-version=1.0.0.svn41 linuxsampler && echo true) | |||
| HAVE_SNDFILE = $(shell pkg-config --exists sndfile && echo true) | |||
| # -------------------------------------------------------------- | |||
| # Check for optional libs (special non-pkgconfig unix tests) | |||
| @@ -281,6 +283,10 @@ ifeq ($(HAVE_FLUIDSYNTH),true) | |||
| BASE_FLAGS += -DHAVE_FLUIDSYNTH | |||
| endif | |||
| ifeq ($(HAVE_FFMPEG),true) | |||
| BASE_FLAGS += -DHAVE_FFMPEG | |||
| endif | |||
| ifeq ($(HAVE_HYLIA),true) | |||
| BASE_FLAGS += -DHAVE_HYLIA | |||
| endif | |||
| @@ -297,6 +303,10 @@ ifeq ($(HAVE_LINUXSAMPLER),true) | |||
| BASE_FLAGS += -DHAVE_LINUXSAMPLER | |||
| endif | |||
| ifeq ($(HAVE_SNDFILE),true) | |||
| BASE_FLAGS += -DHAVE_SNDFILE | |||
| endif | |||
| ifeq ($(HAVE_X11),true) | |||
| BASE_FLAGS += -DHAVE_X11 | |||
| endif | |||
| @@ -309,6 +319,11 @@ LIBLO_FLAGS = $(shell pkg-config --cflags liblo) | |||
| LIBLO_LIBS = $(shell pkg-config --libs liblo) | |||
| endif | |||
| ifeq ($(HAVE_FFMPEG),true) | |||
| FFMPEG_FLAGS = $(shell pkg-config --cflags libavcodec libavformat libavutil) | |||
| FFMPEG_LIBS = $(shell pkg-config --libs libavcodec libavformat libavutil) | |||
| endif | |||
| ifeq ($(HAVE_FLUIDSYNTH),true) | |||
| FLUIDSYNTH_FLAGS = $(shell pkg-config --cflags fluidsynth) | |||
| FLUIDSYNTH_LIBS = $(shell pkg-config --libs fluidsynth) | |||
| @@ -325,6 +340,11 @@ LINUXSAMPLER_LIBS += -lws2_32 | |||
| endif | |||
| endif | |||
| ifeq ($(HAVE_SNDFILE),true) | |||
| SNDFILE_FLAGS = $(shell pkg-config --cflags sndfile) | |||
| SNDFILE_LIBS = $(shell pkg-config --libs sndfile) | |||
| endif | |||
| ifeq ($(HAVE_X11),true) | |||
| X11_FLAGS = $(shell pkg-config --cflags x11) | |||
| X11_LIBS = $(shell pkg-config --libs x11) | |||
| @@ -512,26 +512,12 @@ const char* carla_get_supported_file_extensions() | |||
| #endif | |||
| ; | |||
| #if 0 | |||
| // Audio files | |||
| { | |||
| using namespace water; | |||
| AudioFormatManager afm; | |||
| afm.registerBasicFormats(); | |||
| String waterFormats; | |||
| for (AudioFormat **it=afm.begin(), **end=afm.end(); it != end; ++it) | |||
| { | |||
| const StringArray& exts((*it)->getFileExtensions()); | |||
| for (String *eit=exts.begin(), *eend=exts.end(); eit != eend; ++eit) | |||
| waterFormats += String(";*" + (*eit)).toRawUTF8(); | |||
| } | |||
| retText += waterFormats.toRawUTF8(); | |||
| } | |||
| #ifdef HAVE_SNDFILE | |||
| retText += ";*.aiff;*.flac;*.oga;*.ogg;*.w64;*.wav"; | |||
| #endif | |||
| #ifdef HAVE_FFMPEG | |||
| retText += ";*.3g2;*.3gp;*.aac;*.ac3;*.amr;*.ape;*.mp2;*.mp3;*.mpc;*.wma"; | |||
| #endif | |||
| } | |||
| @@ -24,8 +24,9 @@ TARGETS = \ | |||
| STANDALONE_LIBS = $(MODULEDIR)/carla_engine.a | |||
| STANDALONE_LIBS += $(MODULEDIR)/carla_plugin.a | |||
| STANDALONE_LIBS += $(MODULEDIR)/jackbridge.a | |||
| STANDALONE_LIBS += $(MODULEDIR)/audio_decoder.a | |||
| STANDALONE_LIBS += $(MODULEDIR)/lilv.a | |||
| STANDALONE_LIBS += $(MODULEDIR)/native-plugins.a | |||
| STANDALONE_LIBS += $(MODULEDIR)/rtmempool.a | |||
| @@ -43,6 +43,10 @@ LIBS_win64 = $(MODULEDIR)/jackbridge.win64e.a | |||
| endif | |||
| LINK_FLAGS += $(JACKBRIDGE_LIBS) | |||
| LIBS_native += $(MODULEDIR)/audio_decoder.a | |||
| LINK_FLAGS += $(FFMPEG_LIBS) | |||
| LINK_FLAGS += $(SNDFILE_LIBS) | |||
| LIBS_native += $(MODULEDIR)/lilv.a | |||
| LIBS_posix32 += $(MODULEDIR)/lilv.posix32.a | |||
| LIBS_posix64 += $(MODULEDIR)/lilv.posix64.a | |||
| @@ -9,6 +9,8 @@ | |||
| all: | |||
| clean: | |||
| $(MAKE) clean -C audio_decoder | |||
| $(MAKE) clean -C dgl | |||
| $(MAKE) clean -C lilv | |||
| $(MAKE) clean -C rtaudio | |||
| $(MAKE) clean -C rtmempool | |||
| @@ -0,0 +1,61 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for audio_decoder # | |||
| # -------------------------- # | |||
| # Created by falkTX | |||
| # | |||
| CWD=../.. | |||
| MODULENAME=audio_decoder | |||
| include ../Makefile.mk | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| # BUILD_C_FLAGS += | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| OBJS = \ | |||
| $(OBJDIR)/ad_ffmpeg.c.o \ | |||
| $(OBJDIR)/ad_plugin.c.o \ | |||
| $(OBJDIR)/ad_soundfile.c.o | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| all: $(MODULEDIR)/$(MODULENAME).a | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| clean: | |||
| rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a | |||
| debug: | |||
| $(MAKE) DEBUG=true | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| $(MODULEDIR)/$(MODULENAME).a: $(OBJS) | |||
| -@mkdir -p $(MODULEDIR) | |||
| @echo "Creating $(MODULENAME).a" | |||
| @rm -f $@ | |||
| @$(AR) crs $@ $^ | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| $(OBJDIR)/ad_ffmpeg.c.o: ad_ffmpeg.c | |||
| -@mkdir -p $(OBJDIR) | |||
| @echo "Compiling $<" | |||
| @$(CC) $< $(BUILD_C_FLAGS) $(FFMPEG_FLAGS) -c -o $@ | |||
| $(OBJDIR)/ad_plugin.c.o: ad_plugin.c | |||
| -@mkdir -p $(OBJDIR) | |||
| @echo "Compiling $<" | |||
| @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | |||
| $(OBJDIR)/ad_soundfile.c.o: ad_soundfile.c | |||
| -@mkdir -p $(OBJDIR) | |||
| @echo "Compiling $<" | |||
| @$(CC) $< $(BUILD_C_FLAGS) $(SNDFILE_FLAGS) -c -o $@ | |||
| -include $(OBJS:%.o=%.d) | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| @@ -0,0 +1,127 @@ | |||
| /** | |||
| @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__ | |||
| #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); | |||
| #endif | |||
| @@ -0,0 +1,387 @@ | |||
| /** | |||
| 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 <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,175 @@ | |||
| /** | |||
| 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 <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,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,158 @@ | |||
| /** | |||
| 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 <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <strings.h> | |||
| #include <unistd.h> | |||
| #include <math.h> | |||
| #include "ad_plugin.h" | |||
| #ifdef HAVE_SNDFILE | |||
| #include <sndfile.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; | |||
| } | |||
| #endif | |||
| static const ad_plugin ad_sndfile = { | |||
| #ifdef HAVE_SNDFILE | |||
| &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; | |||
| } | |||
| @@ -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,387 @@ | |||
| /* | |||
| * Carla Native Plugins | |||
| * Copyright (C) 2013-2017 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 2 of | |||
| * the License, or 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 General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the GPL.txt file | |||
| */ | |||
| #ifndef AUDIO_BASE_HPP_INCLUDED | |||
| #define AUDIO_BASE_HPP_INCLUDED | |||
| #include "CarlaThread.hpp" | |||
| #include "CarlaMathUtils.hpp" | |||
| extern "C" { | |||
| #include "audio_decoder/ad.h" | |||
| } | |||
| typedef struct adinfo ADInfo; | |||
| struct AudioFilePool { | |||
| float* buffer[2]; | |||
| uint32_t startFrame; | |||
| uint32_t size; | |||
| #ifdef CARLA_PROPER_CPP11_SUPPORT | |||
| AudioFilePool() | |||
| : buffer{nullptr}, | |||
| startFrame(0), | |||
| size(0) {} | |||
| #else | |||
| AudioFilePool() | |||
| : startFrame(0), | |||
| size(0) | |||
| { | |||
| buffer[0] = buffer[1] = nullptr; | |||
| } | |||
| #endif | |||
| ~AudioFilePool() | |||
| { | |||
| CARLA_ASSERT(buffer[0] == nullptr); | |||
| CARLA_ASSERT(buffer[1] == nullptr); | |||
| CARLA_ASSERT(startFrame == 0); | |||
| CARLA_ASSERT(size == 0); | |||
| } | |||
| void create(const uint32_t sampleRate) | |||
| { | |||
| CARLA_ASSERT(buffer[0] == nullptr); | |||
| CARLA_ASSERT(buffer[1] == nullptr); | |||
| CARLA_ASSERT(startFrame == 0); | |||
| CARLA_ASSERT(size == 0); | |||
| size = sampleRate * 2; | |||
| buffer[0] = new float[size]; | |||
| buffer[1] = new float[size]; | |||
| reset(); | |||
| } | |||
| void destroy() | |||
| { | |||
| CARLA_ASSERT(buffer[0] != nullptr); | |||
| CARLA_ASSERT(buffer[1] != nullptr); | |||
| CARLA_ASSERT(size != 0); | |||
| if (buffer[0] != nullptr) | |||
| { | |||
| delete[] buffer[0]; | |||
| buffer[0] = nullptr; | |||
| } | |||
| if (buffer[1] != nullptr) | |||
| { | |||
| delete[] buffer[1]; | |||
| buffer[1] = nullptr; | |||
| } | |||
| startFrame = 0; | |||
| size = 0; | |||
| } | |||
| void reset() | |||
| { | |||
| startFrame = 0; | |||
| CARLA_SAFE_ASSERT_RETURN(size != 0,); | |||
| carla_zeroFloats(buffer[0], size); | |||
| carla_zeroFloats(buffer[1], size); | |||
| } | |||
| }; | |||
| class AbstractAudioPlayer | |||
| { | |||
| public: | |||
| virtual ~AbstractAudioPlayer() {} | |||
| virtual uint32_t getLastFrame() const = 0; | |||
| }; | |||
| class AudioFileThread : public CarlaThread | |||
| { | |||
| public: | |||
| AudioFileThread(AbstractAudioPlayer* const player, const double sampleRate) | |||
| : CarlaThread("AudioFileThread"), | |||
| kPlayer(player), | |||
| fNeedsRead(false), | |||
| fQuitNow(true), | |||
| fFilePtr(nullptr) | |||
| { | |||
| CARLA_ASSERT(kPlayer != nullptr); | |||
| static bool adInitiated = false; | |||
| if (! adInitiated) | |||
| { | |||
| ad_init(); | |||
| adInitiated = true; | |||
| } | |||
| ad_clear_nfo(&fFileNfo); | |||
| fPool.create(sampleRate); | |||
| } | |||
| ~AudioFileThread() override | |||
| { | |||
| CARLA_ASSERT(fQuitNow); | |||
| CARLA_ASSERT(! isThreadRunning()); | |||
| if (fFilePtr != nullptr) | |||
| ad_close(fFilePtr); | |||
| fPool.destroy(); | |||
| } | |||
| void startNow() | |||
| { | |||
| fNeedsRead = true; | |||
| fQuitNow = false; | |||
| startThread(); | |||
| } | |||
| void stopNow() | |||
| { | |||
| fNeedsRead = false; | |||
| fQuitNow = true; | |||
| stopThread(1000); | |||
| const CarlaMutexLocker cml(fMutex); | |||
| fPool.reset(); | |||
| } | |||
| uint32_t getMaxFrame() const | |||
| { | |||
| return fFileNfo.frames > 0 ? fFileNfo.frames : 0; | |||
| } | |||
| void setNeedsRead() | |||
| { | |||
| fNeedsRead = true; | |||
| } | |||
| bool loadFilename(const char* const filename) | |||
| { | |||
| CARLA_ASSERT(! isThreadRunning()); | |||
| CARLA_ASSERT(filename != nullptr); | |||
| fPool.startFrame = 0; | |||
| // clear old data | |||
| if (fFilePtr != nullptr) | |||
| { | |||
| ad_close(fFilePtr); | |||
| fFilePtr = nullptr; | |||
| } | |||
| ad_clear_nfo(&fFileNfo); | |||
| // open new | |||
| fFilePtr = ad_open(filename, &fFileNfo); | |||
| if (fFilePtr == nullptr) | |||
| return false; | |||
| ad_dump_nfo(99, &fFileNfo); | |||
| if (fFileNfo.frames == 0) | |||
| carla_stderr("L: filename \"%s\" has 0 frames", filename); | |||
| if ((fFileNfo.channels == 1 || fFileNfo.channels == 2) && fFileNfo.frames > 0) | |||
| { | |||
| // valid | |||
| readPoll(); | |||
| return true; | |||
| } | |||
| else | |||
| { | |||
| // invalid | |||
| ad_clear_nfo(&fFileNfo); | |||
| ad_close(fFilePtr); | |||
| fFilePtr = nullptr; | |||
| return false; | |||
| } | |||
| } | |||
| void tryPutData(AudioFilePool& pool) | |||
| { | |||
| CARLA_ASSERT(pool.size == fPool.size); | |||
| if (pool.size != fPool.size) | |||
| return; | |||
| if (! fMutex.tryLock()) | |||
| return; | |||
| //if (pool.startFrame != fPool.startFrame || pool.buffer[0] != fPool.buffer[0] || pool.buffer[1] != fPool.buffer[1]) | |||
| { | |||
| pool.startFrame = fPool.startFrame; | |||
| carla_copyFloats(pool.buffer[0], fPool.buffer[0], fPool.size); | |||
| carla_copyFloats(pool.buffer[1], fPool.buffer[1], fPool.size); | |||
| } | |||
| fMutex.unlock(); | |||
| } | |||
| void readPoll() | |||
| { | |||
| if (fFileNfo.frames <= 0 || fFilePtr == nullptr) | |||
| { | |||
| carla_stderr("R: no song loaded"); | |||
| fNeedsRead = false; | |||
| return; | |||
| } | |||
| int64_t lastFrame = kPlayer->getLastFrame(); | |||
| int64_t readFrame = lastFrame; | |||
| int64_t maxFrame = fFileNfo.frames; | |||
| if (lastFrame >= maxFrame) | |||
| { | |||
| #if 0 | |||
| if (false) | |||
| //if (handlePtr->loopMode) | |||
| { | |||
| carla_stderr("R: DEBUG read loop, lastFrame:%i, maxFrame:%i", lastFrame, maxFrame); | |||
| if (maxFrame >= static_cast<int64_t>(fPool.size)) | |||
| { | |||
| readFrame %= maxFrame; | |||
| } | |||
| else | |||
| { | |||
| readFrame = 0; | |||
| lastFrame -= lastFrame % maxFrame; | |||
| } | |||
| } | |||
| else | |||
| #endif | |||
| { | |||
| carla_stderr("R: transport out of bounds"); | |||
| fNeedsRead = false; | |||
| return; | |||
| } | |||
| } | |||
| // temp data buffer | |||
| const size_t tmpSize = fPool.size * fFileNfo.channels; | |||
| float tmpData[tmpSize]; | |||
| carla_zeroFloats(tmpData, tmpSize); | |||
| { | |||
| carla_stderr("R: poll data - reading at %li:%02li", readFrame/44100/60, (readFrame/44100) % 60); | |||
| ad_seek(fFilePtr, readFrame); | |||
| ssize_t i, j, rv = ad_read(fFilePtr, tmpData, tmpSize); | |||
| i = j = 0; | |||
| // lock, and put data asap | |||
| const CarlaMutexLocker cml(fMutex); | |||
| for (; i < fPool.size && j < rv; ++j) | |||
| { | |||
| if (fFileNfo.channels == 1) | |||
| { | |||
| fPool.buffer[0][i] = tmpData[j]; | |||
| fPool.buffer[1][i] = tmpData[j]; | |||
| i++; | |||
| } | |||
| else | |||
| { | |||
| if (j % 2 == 0) | |||
| { | |||
| fPool.buffer[0][i] = tmpData[j]; | |||
| } | |||
| else | |||
| { | |||
| fPool.buffer[1][i] = tmpData[j]; | |||
| i++; | |||
| } | |||
| } | |||
| } | |||
| #if 0 | |||
| if (false) | |||
| //if (handlePtr->loopMode && i < fPool.size) | |||
| { | |||
| while (i < fPool.size) | |||
| { | |||
| for (j=0; i < fPool.size && j < rv; ++j) | |||
| { | |||
| if (fFileNfo.channels == 1) | |||
| { | |||
| fPool.buffer[0][i] = tmpData[j]; | |||
| fPool.buffer[1][i] = tmpData[j]; | |||
| i++; | |||
| } | |||
| else | |||
| { | |||
| if (j % 2 == 0) | |||
| { | |||
| fPool.buffer[0][i] = tmpData[j]; | |||
| } | |||
| else | |||
| { | |||
| fPool.buffer[1][i] = tmpData[j]; | |||
| i++; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else | |||
| #endif | |||
| { | |||
| for (; i < fPool.size; ++i) | |||
| { | |||
| fPool.buffer[0][i] = 0.0f; | |||
| fPool.buffer[1][i] = 0.0f; | |||
| } | |||
| } | |||
| fPool.startFrame = lastFrame; | |||
| } | |||
| fNeedsRead = false; | |||
| } | |||
| protected: | |||
| void run() override | |||
| { | |||
| while (! fQuitNow) | |||
| { | |||
| const uint32_t lastFrame(kPlayer->getLastFrame()); | |||
| if (fNeedsRead || lastFrame < fPool.startFrame || (lastFrame - fPool.startFrame >= fPool.size*3/4 && lastFrame < fFileNfo.frames)) | |||
| readPoll(); | |||
| else | |||
| carla_msleep(50); | |||
| } | |||
| } | |||
| private: | |||
| AbstractAudioPlayer* const kPlayer; | |||
| bool fNeedsRead; | |||
| bool fQuitNow; | |||
| void* fFilePtr; | |||
| ADInfo fFileNfo; | |||
| AudioFilePool fPool; | |||
| CarlaMutex fMutex; | |||
| }; | |||
| #endif // AUDIO_BASE_HPP_INCLUDED | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * Carla Native Plugins | |||
| * Copyright (C) 2012-2017 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| @@ -12,42 +12,41 @@ | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
| * For a full copy of the GNU General Public License see the GPL.txt file | |||
| */ | |||
| #include "CarlaNative.hpp" | |||
| #if 0 | |||
| #include "CarlaMutex.hpp" | |||
| #include "CarlaString.hpp" | |||
| using namespace water; | |||
| #include "audio-base.hpp" | |||
| // ----------------------------------------------------------------------- | |||
| #define PROGRAM_COUNT 16 | |||
| class AudioFilePlugin : public NativePluginClass | |||
| class AudioFilePlugin : public NativePluginClass, | |||
| public AbstractAudioPlayer | |||
| { | |||
| public: | |||
| AudioFilePlugin(const NativeHostDescriptor* const host) | |||
| : NativePluginClass(host), | |||
| AbstractAudioPlayer(), | |||
| fLoopMode(false), | |||
| fDoProcess(false), | |||
| fLength(0), | |||
| //fThread("AudioFilePluginThread"), | |||
| fReaderBuffer(), | |||
| fReaderMutex(), | |||
| fReader(), | |||
| fReaderSource() | |||
| fLastFrame(0), | |||
| fMaxFrame(0), | |||
| fThread(this, getSampleRate()) | |||
| { | |||
| fReaderBuffer.setSize(2, static_cast<int>(getBufferSize())); | |||
| fPool.create(getSampleRate()); | |||
| } | |||
| ~AudioFilePlugin() override | |||
| { | |||
| //fThread.stopThread(-1); | |||
| fReader = nullptr; | |||
| fReaderSource = nullptr; | |||
| fPool.destroy(); | |||
| fThread.stopNow(); | |||
| } | |||
| uint32_t getLastFrame() const override | |||
| { | |||
| return fLastFrame; | |||
| } | |||
| protected: | |||
| @@ -56,7 +55,7 @@ protected: | |||
| uint32_t getParameterCount() const override | |||
| { | |||
| return 1; | |||
| return 0; // TODO - loopMode | |||
| } | |||
| const NativeParameter* getParameterInfo(const uint32_t index) const override | |||
| @@ -97,77 +96,94 @@ protected: | |||
| if (index != 0) | |||
| return; | |||
| const bool loopMode(value > 0.5f); | |||
| bool b = (value > 0.5f); | |||
| if (fLoopMode == loopMode) | |||
| if (b == fLoopMode) | |||
| return; | |||
| fLoopMode = loopMode; | |||
| const CarlaMutexLocker cml(fReaderMutex); | |||
| if (fReaderSource != nullptr) | |||
| fReaderSource->setLooping(loopMode); | |||
| fLoopMode = b; | |||
| fThread.setNeedsRead(); | |||
| } | |||
| void setCustomData(const char* const key, const char* const value) override | |||
| { | |||
| CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); | |||
| CARLA_SAFE_ASSERT_RETURN(value != nullptr && value[0] != '\0',); | |||
| if (std::strcmp(key, "file") != 0) | |||
| return; | |||
| _loadAudioFile(value); | |||
| loadFilename(value); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Plugin process calls | |||
| void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override | |||
| void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent*, uint32_t) override | |||
| { | |||
| const NativeTimeInfo* const timePos(getTimeInfo()); | |||
| float* const out1(outBuffer[0]); | |||
| float* const out2(outBuffer[1]); | |||
| float* out1 = outBuffer[0]; | |||
| float* out2 = outBuffer[1]; | |||
| if (fLength == 0 || ! fDoProcess) | |||
| if (! fDoProcess) | |||
| { | |||
| //carla_stderr("P: no process"); | |||
| carla_zeroFloats(out1, iframes); | |||
| carla_zeroFloats(out2, iframes); | |||
| fLastFrame = timePos->frame; | |||
| carla_zeroFloats(out1, frames); | |||
| carla_zeroFloats(out2, frames); | |||
| return; | |||
| } | |||
| const int64_t nextReadPos(fLoopMode ? (static_cast<int64_t>(timePos->frame) % fLength) : static_cast<int64_t>(timePos->frame)); | |||
| // not playing | |||
| if (! timePos->playing) | |||
| { | |||
| //carla_stderr("P: not playing"); | |||
| carla_zeroFloats(out1, iframes); | |||
| carla_zeroFloats(out2, iframes); | |||
| fLastFrame = timePos->frame; | |||
| const CarlaMutexLocker cml(fReaderMutex); | |||
| if (fReaderSource != nullptr) | |||
| fReaderSource->setNextReadPosition(nextReadPos); | |||
| if (timePos->frame == 0 && fLastFrame > 0) | |||
| fThread.setNeedsRead(); | |||
| carla_zeroFloats(out1, frames); | |||
| carla_zeroFloats(out2, frames); | |||
| return; | |||
| } | |||
| const CarlaMutexLocker cml(fReaderMutex); | |||
| fThread.tryPutData(fPool); | |||
| // out of reach | |||
| if (timePos->frame + frames < fPool.startFrame || timePos->frame >= fMaxFrame) /*&& ! loopMode)*/ | |||
| { | |||
| //carla_stderr("P: out of reach"); | |||
| fLastFrame = timePos->frame; | |||
| if (fReaderSource != nullptr) | |||
| fReaderSource->setNextReadPosition(nextReadPos); | |||
| if (timePos->frame + frames < fPool.startFrame) | |||
| fThread.setNeedsRead(); | |||
| if (fReader == nullptr) | |||
| carla_zeroFloats(out1, frames); | |||
| carla_zeroFloats(out2, frames); | |||
| return; | |||
| } | |||
| fReader->read(&fReaderBuffer, 0, iframes, nextReadPos, true, true); | |||
| int64_t poolFrame = (int64_t)timePos->frame - fPool.startFrame; | |||
| int64_t poolSize = fPool.size; | |||
| carla_copyFloats(out1, fReaderBuffer.getReadPointer(0), frames); | |||
| carla_copyFloats(out2, fReaderBuffer.getReadPointer(1), frames); | |||
| for (uint32_t i=0; i < frames; ++i, ++poolFrame) | |||
| { | |||
| if (poolFrame >= 0 && poolFrame < poolSize) | |||
| { | |||
| out1[i] = fPool.buffer[0][poolFrame]; | |||
| out2[i] = fPool.buffer[1][poolFrame]; | |||
| // reset | |||
| fPool.buffer[0][poolFrame] = 0.0f; | |||
| fPool.buffer[1][poolFrame] = 0.0f; | |||
| } | |||
| else | |||
| { | |||
| out1[i] = 0.0f; | |||
| out2[i] = 0.0f; | |||
| } | |||
| } | |||
| fLastFrame = timePos->frame; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| @@ -184,85 +200,41 @@ protected: | |||
| uiClosed(); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Plugin dispatcher calls | |||
| void bufferSizeChanged(const uint32_t bufferSize) override | |||
| { | |||
| fReaderBuffer.setSize(2, static_cast<int>(bufferSize)); | |||
| } | |||
| private: | |||
| bool fLoopMode; | |||
| bool fDoProcess; | |||
| int64_t fLength; | |||
| //TimeSliceThread fThread; | |||
| uint32_t fLastFrame; | |||
| uint32_t fMaxFrame; | |||
| AudioSampleBuffer fReaderBuffer; | |||
| CarlaMutex fReaderMutex; | |||
| AudioFilePool fPool; | |||
| AudioFileThread fThread; | |||
| ScopedPointer<AudioFormatReader> fReader; | |||
| ScopedPointer<AudioFormatReaderSource> fReaderSource; | |||
| void _loadAudioFile(const char* const filename) | |||
| void loadFilename(const char* const filename) | |||
| { | |||
| carla_stdout("AudioFilePlugin::loadFilename(\"%s\")", filename); | |||
| fDoProcess = false; | |||
| fLength = 0; | |||
| CARLA_ASSERT(filename != nullptr); | |||
| carla_debug("AudioFilePlugin::loadFilename(\"%s\")", filename); | |||
| //fThread.stopThread(-1); | |||
| fThread.stopNow(); | |||
| if (filename == nullptr || *filename == '\0') | |||
| { | |||
| fReaderMutex.lock(); | |||
| AudioFormatReader* const reader(fReader.release()); | |||
| AudioFormatReaderSource* const readerSource(fReaderSource.release()); | |||
| fReaderMutex.unlock(); | |||
| delete readerSource; | |||
| delete reader; | |||
| } | |||
| const String jfilename = String(CharPointer_UTF8(filename)); | |||
| File file(jfilename); | |||
| if (! file.existsAsFile()) | |||
| fDoProcess = false; | |||
| fMaxFrame = 0; | |||
| return; | |||
| } | |||
| AudioFormatManager& afm(getAudioFormatManagerInstance()); | |||
| AudioFormat* const format(afm.findFormatForFileExtension(file.getFileExtension())); | |||
| CARLA_SAFE_ASSERT_RETURN(format != nullptr,); | |||
| if (MemoryMappedAudioFormatReader* const memReader = format->createMemoryMappedReader(file)) | |||
| if (fThread.loadFilename(filename)) | |||
| { | |||
| memReader->mapEntireFile(); | |||
| fReader = memReader; | |||
| carla_stdout("Using memory mapped read file"); | |||
| fThread.startNow(); | |||
| fMaxFrame = fThread.getMaxFrame(); | |||
| fDoProcess = true; | |||
| } | |||
| else | |||
| { | |||
| AudioFormatReader* const reader(afm.createReaderFor(file)); | |||
| CARLA_SAFE_ASSERT_RETURN(reader != nullptr,); | |||
| // this code can be used for very large files | |||
| //fThread.startThread(); | |||
| //BufferingAudioReader* const bufferingReader(new BufferingAudioReader(reader, fThread, getSampleRate()*2)); | |||
| //bufferingReader->setReadTimeout(50); | |||
| AudioFormatReaderSource* const readerSource(new AudioFormatReaderSource(/*bufferingReader*/reader, false)); | |||
| readerSource->setLooping(fLoopMode); | |||
| fReaderSource = readerSource; | |||
| fReader = reader; | |||
| carla_stdout("Using regular read file"); | |||
| fDoProcess = false; | |||
| fMaxFrame = 0; | |||
| } | |||
| fLength = fReader->lengthInSamples; | |||
| fDoProcess = true; | |||
| } | |||
| PluginClassEND(AudioFilePlugin) | |||
| @@ -273,14 +245,16 @@ private: | |||
| static const NativePluginDescriptor audiofileDesc = { | |||
| /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY, | |||
| /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_HAS_UI | |||
| |NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE), | |||
| /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE | |||
| |NATIVE_PLUGIN_HAS_UI | |||
| |NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE | |||
| |NATIVE_PLUGIN_USES_TIME), | |||
| /* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING, | |||
| /* audioIns */ 0, | |||
| /* audioOuts */ 2, | |||
| /* midiIns */ 0, | |||
| /* midiOuts */ 0, | |||
| /* paramIns */ 1, | |||
| /* paramIns */ 0, // TODO - loopMode | |||
| /* paramOuts */ 0, | |||
| /* name */ "Audio File", | |||
| /* label */ "audiofile", | |||
| @@ -291,15 +265,13 @@ static const NativePluginDescriptor audiofileDesc = { | |||
| // ----------------------------------------------------------------------- | |||
| #endif | |||
| CARLA_EXPORT | |||
| void carla_register_native_plugin_audiofile(); | |||
| CARLA_EXPORT | |||
| void carla_register_native_plugin_audiofile() | |||
| { | |||
| //carla_register_native_plugin(&audiofileDesc); | |||
| carla_register_native_plugin(&audiofileDesc); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| @@ -50,6 +50,8 @@ endif | |||
| LIBS = $(MODULEDIR)/carla_engine_plugin.a | |||
| LIBS += $(MODULEDIR)/carla_plugin.a | |||
| LIBS += $(MODULEDIR)/jackbridge.a | |||
| LIBS += $(MODULEDIR)/audio_decoder.a | |||
| LIBS += $(MODULEDIR)/lilv.a | |||
| LIBS += $(MODULEDIR)/native-plugins.a | |||
| LIBS += $(MODULEDIR)/rtmempool.a | |||
| @@ -72,25 +74,13 @@ LINK_FLAGS += $(NATIVE_PLUGINS_LIBS) | |||
| LINK_FLAGS += $(RTMEMPOOL_LIBS) | |||
| LINK_FLAGS += $(WATER_LIBS) | |||
| ifeq ($(HAVE_LIBLO),true) | |||
| LINK_FLAGS += $(FFMPEG_LIBS) | |||
| LINK_FLAGS += $(LIBLO_LIBS) | |||
| endif | |||
| ifeq ($(HAVE_LIBMAGIC),true) | |||
| LINK_FLAGS += $(MAGIC_LIBS) | |||
| endif | |||
| ifeq ($(HAVE_FLUIDSYNTH),true) | |||
| LINK_FLAGS += $(FLUIDSYNTH_LIBS) | |||
| endif | |||
| ifeq ($(HAVE_LINUXSAMPLER),true) | |||
| LINK_FLAGS += $(LINUXSAMPLER_LIBS) | |||
| endif | |||
| ifeq ($(HAVE_X11),true) | |||
| LINK_FLAGS += $(SNDFILE_LIBS) | |||
| LINK_FLAGS += $(X11_LIBS) | |||
| endif | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||