Browse Source

Go back to old audiofile plugin from 1.x release

This adds back audio_decoder library as module as well.
Likely breaks bridges build, to be handled soon.
tags/1.9.8
falkTX 8 years ago
parent
commit
8691117503
16 changed files with 1585 additions and 155 deletions
  1. +1
    -0
      Makefile
  2. +20
    -0
      source/Makefile.mk
  3. +5
    -19
      source/backend/CarlaUtils.cpp
  4. +2
    -1
      source/backend/Makefile
  5. +4
    -0
      source/bridges-plugin/Makefile
  6. +2
    -0
      source/modules/Makefile
  7. +61
    -0
      source/modules/audio_decoder/Makefile
  8. +127
    -0
      source/modules/audio_decoder/ad.h
  9. +387
    -0
      source/modules/audio_decoder/ad_ffmpeg.c
  10. +175
    -0
      source/modules/audio_decoder/ad_plugin.c
  11. +64
    -0
      source/modules/audio_decoder/ad_plugin.h
  12. +158
    -0
      source/modules/audio_decoder/ad_soundfile.c
  13. +95
    -0
      source/modules/audio_decoder/ffcompat.h
  14. +387
    -0
      source/native-plugins/audio-base.hpp
  15. +93
    -121
      source/native-plugins/audio-file.cpp
  16. +4
    -14
      source/plugin/Makefile

+ 1
- 0
Makefile View File

@@ -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


+ 20
- 0
source/Makefile.mk View File

@@ -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)


+ 5
- 19
source/backend/CarlaUtils.cpp View File

@@ -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
}



+ 2
- 1
source/backend/Makefile View File

@@ -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


+ 4
- 0
source/bridges-plugin/Makefile View File

@@ -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


+ 2
- 0
source/modules/Makefile View File

@@ -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


+ 61
- 0
source/modules/audio_decoder/Makefile View File

@@ -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)

# ----------------------------------------------------------------------------------------------------------------------------

+ 127
- 0
source/modules/audio_decoder/ad.h View File

@@ -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

+ 387
- 0
source/modules/audio_decoder/ad_ffmpeg.c View File

@@ -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;
}

+ 175
- 0
source/modules/audio_decoder/ad_plugin.c View File

@@ -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;
}

+ 64
- 0
source/modules/audio_decoder/ad_plugin.h View File

@@ -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

+ 158
- 0
source/modules/audio_decoder/ad_soundfile.c View File

@@ -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;
}

+ 95
- 0
source/modules/audio_decoder/ffcompat.h View File

@@ -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 */

+ 387
- 0
source/native-plugins/audio-base.hpp View File

@@ -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

+ 93
- 121
source/native-plugins/audio-file.cpp View File

@@ -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);
}

// -----------------------------------------------------------------------

+ 4
- 14
source/plugin/Makefile View File

@@ -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

# ----------------------------------------------------------------------------------------------------------------------------



Loading…
Cancel
Save