From 6dec07e785db93ea9d2702b660b885d678dc87e4 Mon Sep 17 00:00:00 2001 From: august black Date: Wed, 28 Jul 2010 09:14:17 +0000 Subject: adding the readanysf~ again svn path=/trunk/externals/august/readanysf~/; revision=13732 --- Makefile | 79 ++- README | 13 +- READMEmacpkg.txt | 11 + embed-MacOSX-dependencies.sh | 2 +- readanysf~-help.pd | 6 +- screengrab0.40.png | Bin 0 -> 31276 bytes src/FifoAudioFrames.cpp | 140 ++++++ src/FifoAudioFrames.h | 41 ++ src/FifoVideoFrames.cpp | 120 +++++ src/FifoVideoFrames.h | 43 ++ src/ReadMedia.cpp | 1132 ++++++++++++++++++++++++++++++++++++++++++ src/ReadMedia.h | 220 ++++++++ src/readanysf~.cpp | 507 +++++++++++++++++++ stress.pd | 53 ++ 14 files changed, 2342 insertions(+), 25 deletions(-) create mode 100644 READMEmacpkg.txt create mode 100644 screengrab0.40.png create mode 100644 src/FifoAudioFrames.cpp create mode 100644 src/FifoAudioFrames.h create mode 100644 src/FifoVideoFrames.cpp create mode 100644 src/FifoVideoFrames.h create mode 100644 src/ReadMedia.cpp create mode 100644 src/ReadMedia.h create mode 100644 src/readanysf~.cpp create mode 100644 stress.pd diff --git a/Makefile b/Makefile index d306b87..416b6f9 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,19 @@ GAVLPATH=/usr/local/include PDPATH=/usr/local/include + +VERSION=0.40 +UNAME := $(shell uname) + +ifeq ($(UNAME), Linux) +TARGET=pd_linux +else +# assume darwin here +GAVLPATH=/sw/include +PDPATH=/Applications/Pd-extended.app/Contents/Resources/include/ +TARGET=pd_darwin +endif + ############################################## LBITS := $(shell getconf LONG_BIT) ifeq ($(LBITS),64) @@ -14,27 +27,67 @@ else CFLAGS = -I./ -I$(GAVLPATH) -I$(GAVLPATH)/gavl -I$(GAVLPATH)/gmerlin -I$(PDPATH) -Wall endif + +ifeq ($(UNAME), Linux) +STRIP=strip --strip-unneeded +# optimizations? +#CFLAGS += -O1 -funroll-loops -fomit-frame-pointer \ +# -Wall -W -Wshadow \ +# -Wno-unused -Wno-parentheses -Wno-switch LDFLAGS = -L/usr/local/lib -lpthread -lgavl -lgmerlin_avdec +else +# assume darwin here +STRIP=strip -x +CFLAGS += -I/sw/include -I$(externals_src)/pdp/include -DMACOSX -DUNIX -Dunix +#LDFLAGS += -bundle -bundle_loader $(pd_src)/bin/pd -undefined dynamic_lookup \ +# -L/sw/lib -weak_framework Carbon -lc -L/sw/lib -lgavl -lgmerlin_avdec +LDFLAGS = -dynamiclib -undefined dynamic_lookup -read_only_relocs warning -L/sw/lib -lgavl -lgmerlin_avdec +# os 10.4 +#CFLAGS += -mmacosx-version-min=10.4 -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk +#LDFLAGS = -L/sw/lib -lgavl -lgmerlin_avdec \ +# -dynamiclib -undefined dynamic_lookup -lsupc++ -mmacosx-version-min=10.4 \ +# -lSystem.B -arch i386 -isysroot /Developer/SDKs/MacOSX10.4u.sdk +endif + -LINUXCFLAGS = -O1 -funroll-loops -fomit-frame-pointer \ - -Wall -W -Wshadow -Wstrict-prototypes \ - -Wno-unused -Wno-parentheses -Wno-switch -all: pd_linux +all: $(TARGET) -pd_linux: readanysf~.cpp Readsf.cpp Readsf.h objs/FifoAudioFrames.o objs/Readsf.o FifoAudioFrames.h FifoAudioFrames.cpp +pd_linux: src/readanysf~.cpp objs/FifoVideoFrames.o objs/FifoAudioFrames.o objs/ReadMedia.o g++ -shared -o readanysf~.pd_linux $(CFLAGS) $(LDFLAGS) \ - readanysf~.cpp \ + src/readanysf~.cpp \ objs/FifoAudioFrames.o \ - objs/Readsf.o - strip --strip-unneeded readanysf~.pd_linux + objs/FifoVideoFrames.o \ + objs/ReadMedia.o + $(STRIP) readanysf~.pd_linux + +pd_darwin: src/readanysf~.cpp objs/FifoVideoFrames.o objs/FifoAudioFrames.o objs/ReadMedia.o + g++ $(LDFLAGS) -o readanysf~.pd_darwin $(CFLAGS) \ + src/readanysf~.cpp \ + objs/FifoAudioFrames.o \ + objs/FifoVideoFrames.o \ + objs/ReadMedia.o + $(STRIP) readanysf~.pd_darwin + mkdir -p readanysf~$(VERSION)_MacOSX-Intel + mkdir -p readanysf~$(VERSION)_MacOSX-Intel/readanysf~ + cp readanysf~.pd_darwin readanysf~-help.pd readanysf~$(VERSION)_MacOSX-Intel/readanysf~ + cp READMEmacpkg.txt anysndfiler.pd readanysf~$(VERSION)_MacOSX-Intel/ + mv readanysf~$(VERSION)_MacOSX-Intel/READMEmacpkg.txt readanysf~$(VERSION)_MacOSX-Intel/README.txt + ./embed-MacOSX-dependencies.sh readanysf~$(VERSION)_MacOSX-Intel/readanysf~ + tar -cvf readanysf~$(VERSION)_MacOSX-Intel.tar readanysf~$(VERSION)_MacOSX-Intel/ + gzip readanysf~$(VERSION)_MacOSX-Intel.tar + +objs/ReadMedia.o: src/ReadMedia.cpp src/ReadMedia.h objs/FifoAudioFrames.o objs/FifoVideoFrames.o + g++ -c -o objs/ReadMedia.o src/ReadMedia.cpp $(CFLAGS) -objs/Readsf.o: Readsf.cpp Readsf.h FifoAudioFrames.h - g++ -c -o objs/Readsf.o Readsf.cpp $(CFLAGS) +objs/FifoAudioFrames.o: src/FifoAudioFrames.cpp src/FifoAudioFrames.h + g++ -c -o objs/FifoAudioFrames.o src/FifoAudioFrames.cpp $(CFLAGS) -objs/FifoAudioFrames.o: FifoAudioFrames.cpp FifoAudioFrames.h - g++ -c -o objs/FifoAudioFrames.o FifoAudioFrames.cpp $(CFLAGS) +objs/FifoVideoFrames.o: src/FifoVideoFrames.cpp src/FifoVideoFrames.h + g++ -c -o objs/FifoVideoFrames.o src/FifoVideoFrames.cpp $(CFLAGS) clean: - rm objs/*.o readanysf~.pd_linux readanysf~.pd_darwin + if [ -d readanysf~$(VERSION)_MacOSX-Intel ]; then rm -rf readanysf~$(VERSION)_MacOSX-Intel; fi; + if [ -f readanysf~$(VERSION)_MacOSX-Intel.tar.gz ]; then rm -rf readanysf~$(VERSION)_MacOSX-Intel.tar.gz; fi; + rm -f objs/*.o readanysf~.pd_* diff --git a/README b/README index 7af1c8d..3d7118b 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ readanysf~, a puredata external for reading multiple file formats. ----------------------------------------------------------------- -© august black 2003 - 2009 +© august black 2003 - 2010 licensed under the GNU GPL v2 @@ -31,8 +31,7 @@ you will need: sudo make install -If you didn't change anything in the ./configure of gavl or gmerlin, AND your PD header files are in /usr/local/include you can just type make. Otherwise, open the Makefile with your favorite text editor and change the first two variables to suit your system. - +After installing gavl and gmerlin_avdecode, you should just be able to type make. ---------------------------------------------------------------------------------------- @@ -40,15 +39,9 @@ Mac users: you can install gavl/gmerlin-avdecoder either by source as above with linux or through fink. - then, first try "make -f Makefile.darwin" - + then, do "make" and let me know if it doesn't work - If that doesn't work ...then, if you have a i386 machine with Mac OS.X 10.4 or greater, do: - - export LIBRARY_PATH=/sw/lib:$LIBRARY_PATH; make -f Makefile.darwin_i386_10.4 - - I don't have a mac...so I can't tell what is going on there. diff --git a/READMEmacpkg.txt b/READMEmacpkg.txt new file mode 100644 index 0000000..2e43e2a --- /dev/null +++ b/READMEmacpkg.txt @@ -0,0 +1,11 @@ + +http://aug.ment.org/readanysf/ + +by August Black + +-- +To install, drag the readanysf~ folder and anysndfiler.pd to /Library/Pd. Then you can create [readanysf~] anywhere you need it. + +If /Library/Pd does not exist, just create it. + +This build put together by Hans-Christoph Steiner, post to the pd-list with questions. diff --git a/embed-MacOSX-dependencies.sh b/embed-MacOSX-dependencies.sh index f8275a4..0612b0c 100755 --- a/embed-MacOSX-dependencies.sh +++ b/embed-MacOSX-dependencies.sh @@ -5,7 +5,7 @@ # LIB_DIR=/Library/Pd/readanysf~ -PD_APP_LIB=. +PD_APP_LIB=$1 echo " " diff --git a/readanysf~-help.pd b/readanysf~-help.pd index 1920cd0..506d1b1 100644 --- a/readanysf~-help.pd +++ b/readanysf~-help.pd @@ -3,7 +3,7 @@ #X obj 28 321 *~ 0; #X obj 69 321 *~ 0; #X obj 579 94 vsl 15 50 0.01 1.3 1 0 empty empty empty 0 -8 0 8 -241291 --1 -1 4411 0; +-1 -1 4300 0; #X msg 112 104 play; #X msg 193 103 pause; #X msg 276 109 speed \$1; @@ -68,6 +68,8 @@ blocksize.; #X text 332 219 send cache \, length output every X ticks; #X text 330 232 "tick 1" sends out every single dsp cycle; #X text 648 202 loop on/off; +#X floatatom 75 502 0 0 0 0 - - -; +#X obj 75 439 env~ 16384 8192; #X connect 1 0 0 0; #X connect 2 0 0 1; #X connect 3 0 11 0; @@ -111,3 +113,5 @@ blocksize.; #X connect 53 0 52 0; #X connect 54 0 44 0; #X connect 55 0 44 0; +#X connect 60 0 59 0; +#X connect 60 0 59 0; diff --git a/screengrab0.40.png b/screengrab0.40.png new file mode 100644 index 0000000..a83017b Binary files /dev/null and b/screengrab0.40.png differ diff --git a/src/FifoAudioFrames.cpp b/src/FifoAudioFrames.cpp new file mode 100644 index 0000000..a3bc682 --- /dev/null +++ b/src/FifoAudioFrames.cpp @@ -0,0 +1,140 @@ +#include "FifoAudioFrames.h" + +/* +#define RESET 0 +#define BRIGHT 1 +#define DIM 2 +#define UNDERLINE 3 +#define BLINK 4 +#define REVERSE 7 +#define HIDDEN 8 + +#define BLACK 0 +#define RED 1 +#define GREEN 2 +#define YELLOW 3 +#define BLUE 4 +#define MAGENTA 5 +#define CYAN 6 +#define WHITE 7 +void textcolor(int attr, int fg, int bg) { + char command[13]; + // Command is the control command to the terminal + sprintf(command, "%c[%d;%d;%dm", 0x1B, attr, fg + 30, bg + 40); + printf("%s", command); +} +*/ + + +FifoAudioFrames::FifoAudioFrames(int s, gavl_audio_format_t * f) { + size = s ; + start = 0 ; + end = 0 ; + count = 0; + format = new gavl_audio_format_t; + gavl_audio_format_copy(format, f); + fifoPtr = new gavl_audio_frame_t * [size] ; + for(int i=0; i < size; i++) { + fifoPtr[i] = gavl_audio_frame_create( format ); + } + pthread_mutex_init (&mut, 0); + //printf("FifoAudioFrames::size=%d\n", size); +} + +FifoAudioFrames::~FifoAudioFrames() { + //printf("deleting fifo \n"); + for(int i=0; i < size; i++) { + gavl_audio_frame_destroy( fifoPtr[i] ); + } + delete format; + delete[] fifoPtr; + + pthread_mutex_destroy (&mut); + //printf("deleted fifo \n"); +} + +// empty the fifo of any content +void FifoAudioFrames::Flush() { + pthread_mutex_lock (&mut); + start = 0 ; + end = 0 ; + count = 0; + //printf("FifoAudioFrames::flushed size=%d\n", count); + pthread_mutex_unlock (&mut); +} + +// push an element onto the FifoAudioFrames +bool FifoAudioFrames::Append( gavl_audio_frame_t * source) { + bool ret = false; + //Dump("Appending a frame "); + pthread_mutex_lock (&mut); + if ( count < size ) { // if there is room for one more + int vs = gavl_audio_frame_copy(format, fifoPtr[end], source, 0,0, format->samples_per_frame, format->samples_per_frame) ; + fifoPtr[end]->timestamp = source->timestamp; + fifoPtr[end]->valid_samples =vs; + if (++end >= size) + end = 0; + count++; + ret = true; + } // no room in fifo, return false + pthread_mutex_unlock (&mut); + //Dump("Appended a frame "); + return ret; +} + +// get an element off the FifoAudioFrames +bool FifoAudioFrames::Get( gavl_audio_frame_t * dest) { + bool ret = false; + //Dump("Getting a frame "); + pthread_mutex_lock (&mut); + if ( count > 0 ) { // if there any items in the fifo + int vs = gavl_audio_frame_copy(format, dest, fifoPtr[start], 0, 0, format->samples_per_frame, format->samples_per_frame) ; + dest->timestamp = fifoPtr[start]->timestamp; + dest->valid_samples = vs; + if ( ++start >= size ) + start = 0; + count--; + ret = true; + } + pthread_mutex_unlock (&mut); + //Dump("Got a frame "); + return ret; +} +/* +void FifoAudioFrames::Dump( char * c) { + int i,j = 0; + pthread_mutex_lock (&mut); + printf("%s -----------------------\n", c); + for( i=0;i= size) + // j = j -size; + j=i; + if ( j == start) + textcolor(BRIGHT, GREEN, BLACK); + if (j == end) + textcolor(BRIGHT, RED, BLACK); + if (j == end && j == start) + textcolor(BRIGHT, YELLOW, BLACK); + printf("[%d]=%ld ", j, fifoPtr[j]->timestamp); + textcolor(RESET, BLACK, WHITE); + } + printf("\n"); + printf("start=%02d, end=%02d, count=%02d\n", start,end,count); + printf("-----------------------\n"); + pthread_mutex_unlock (&mut); +} +*/ +bool FifoAudioFrames::FreeSpace() { + bool ret = false; + //printf("asking for free space on audio fifo\n"); + pthread_mutex_lock(&mut); + ret = (count < size); + pthread_mutex_unlock (&mut); + return ret; +} +bool FifoAudioFrames::isEmpty() { bool c; pthread_mutex_lock(&mut); c = (count == 0) ; pthread_mutex_unlock (&mut); return c; } +bool FifoAudioFrames::isFull() { bool c; pthread_mutex_lock(&mut); c = (count == size ); pthread_mutex_unlock (&mut); return c; } +gavl_audio_format_t * FifoAudioFrames::getFormat() { return format; }; +float FifoAudioFrames::getSizePercentage() { float ret; pthread_mutex_lock(&mut);ret = count / (float) size; pthread_mutex_unlock (&mut); return ret;}; + diff --git a/src/FifoAudioFrames.h b/src/FifoAudioFrames.h new file mode 100644 index 0000000..eea7a66 --- /dev/null +++ b/src/FifoAudioFrames.h @@ -0,0 +1,41 @@ +#ifndef _FIFOAUDIOFRAMES_H_ +#define _FIFOAUDIOFRAMES_H_ + +#include // memcpy +#include +#include + +#ifndef _AVDEC_H_ +#define _AVDEC_H_ +extern "C" { +#include +} +#endif + +class FifoAudioFrames { + public: + FifoAudioFrames(int s, gavl_audio_format_t * format) ; + ~FifoAudioFrames();// { delete [] fifoPtr; } + bool Append( gavl_audio_frame_t * af); + bool Get( gavl_audio_frame_t * af) ; // pop an element off the fifo + void Flush(); + //void Dump(char *c); + bool FreeSpace(); + bool isEmpty(); + bool isFull(); + void setDebug( bool b); + gavl_audio_format_t * getFormat(); + float getSizePercentage(); + private: + int size ; // Number of elements on FifoAudioFrames + int start ; + int end ; + int count; + gavl_audio_frame_t ** fifoPtr ; + gavl_audio_format_t * format; + pthread_mutex_t mut; +} ; + + +#endif + diff --git a/src/FifoVideoFrames.cpp b/src/FifoVideoFrames.cpp new file mode 100644 index 0000000..29975dc --- /dev/null +++ b/src/FifoVideoFrames.cpp @@ -0,0 +1,120 @@ +#include "FifoVideoFrames.h" + +FifoVideoFrames::FifoVideoFrames(int s, gavl_video_format_t * f) { + size = s ; + start = 0 ; + end = 0 ; + count = 0; + format = new gavl_video_format_t; + gavl_video_format_copy(format, f); + fifoPtr = new gavl_video_frame_t * [size] ; + for(int i=0; i < size; i++) { + fifoPtr[i] = gavl_video_frame_create( format ); + } + pthread_mutex_init (&mut, 0); +} + +FifoVideoFrames::~FifoVideoFrames() { + //printf("deleting fifo \n"); + for(int i=0; i < size; i++) { + gavl_video_frame_destroy( fifoPtr[i] ); + } + delete format; + delete[] fifoPtr; + pthread_mutex_destroy (&mut); + //printf("deleted fifo \n"); +} + +// empty the fifo of any content +void FifoVideoFrames::Flush() { + pthread_mutex_lock (&mut); + start = 0 ; + end = 0 ; + count = 0; + //printf("FifoVideoFrames::flushed size=%d\n", count); + pthread_mutex_unlock (&mut); +} + +// push an element onto the FifoVideoFrames +bool FifoVideoFrames::Append( gavl_video_frame_t * source) { + bool ret = false; + //Dump("Appending a frame "); + pthread_mutex_lock (&mut); + if ( count < size ) { // if there is room for one more + gavl_video_frame_copy(format, fifoPtr[end], source) ; + fifoPtr[end]->timestamp = source->timestamp; + fifoPtr[end]->duration = source->duration; + if (++end >= size) + end = 0; + count++; + ret = true; + } // no room in fifo, return false + pthread_mutex_unlock (&mut); + //Dump("Appended a frame "); + return ret; +} +// remove an element off the front FifoVideoFrames +bool FifoVideoFrames::Get( ) { + bool ret = false; + pthread_mutex_lock (&mut); + if ( count > 0 ) { // if there any items in the fifo + if ( ++start >= size ) + start = 0; + count--; + ret = true; + } + pthread_mutex_unlock (&mut); + //Dump("Got a frame "); + return ret; +} + +// get an element off the FifoVideoFrames +bool FifoVideoFrames::Get( gavl_video_frame_t * dest) { + bool ret = false; + //Dump("Getting a frame "); + pthread_mutex_lock (&mut); + if ( count > 0 ) { // if there any items in the fifo + gavl_video_frame_copy(format, dest, fifoPtr[start]) ; + dest->timestamp = fifoPtr[start]->timestamp; + dest->duration = fifoPtr[start]->duration; + if ( ++start >= size ) + start = 0; + count--; + ret = true; + } + pthread_mutex_unlock (&mut); + //Dump("Got a frame "); + return ret; +} +/* +void FifoVideoFrames::Dump( char * c) { + int i,j = 0; + pthread_mutex_lock (&mut); + printf("%s -----------------------\n", c); + for( i=0;i= size) + // j = j -size; + j=i; + if ( j == start) + textcolor(BRIGHT, GREEN, BLACK); + if (j == end) + textcolor(BRIGHT, RED, BLACK); + if (j == end && j == start) + textcolor(BRIGHT, YELLOW, BLACK); + printf("[%d]=%ld ", j, fifoPtr[j]->timestamp); + textcolor(RESET, BLACK, WHITE); + } + printf("\n"); + printf("start=%02d, end=%02d, count=%02d\n", start,end,count); + printf("-----------------------\n"); + pthread_mutex_unlock (&mut); +} +*/ +bool FifoVideoFrames::FreeSpace() { bool ret; pthread_mutex_lock(&mut); ret = (count < size);pthread_mutex_unlock (&mut); return ret;} +bool FifoVideoFrames::isEmpty() { bool c; pthread_mutex_lock(&mut); c = (count == 0) ; pthread_mutex_unlock (&mut); return c; } +bool FifoVideoFrames::isFull() { bool c; pthread_mutex_lock(&mut); c = (count == size ); pthread_mutex_unlock (&mut); return c; } +gavl_video_format_t * FifoVideoFrames::getFormat() { return format; }; +float FifoVideoFrames::getSizePercentage() { float ret; pthread_mutex_lock(&mut);ret = count / (float) size; pthread_mutex_unlock (&mut); return ret;}; +int FifoVideoFrames::getSize() { int ret; pthread_mutex_lock(&mut);ret = count; pthread_mutex_unlock (&mut); return ret;}; + diff --git a/src/FifoVideoFrames.h b/src/FifoVideoFrames.h new file mode 100644 index 0000000..ee5beba --- /dev/null +++ b/src/FifoVideoFrames.h @@ -0,0 +1,43 @@ +#ifndef _FIFOVIDEOFRAMES_H_ +#define _FIFOVIDEOFRAMES_H_ + +#include // memcpy +#include +#include + +#ifndef _AVDEC_H_ +#define _AVDEC_H_ +extern "C" { +#include +} +#endif + +class FifoVideoFrames { + public: + FifoVideoFrames(int s, gavl_video_format_t * format) ; + ~FifoVideoFrames();// { delete [] fifoPtr; } + bool Append( gavl_video_frame_t * af); + bool Get( gavl_video_frame_t * af) ; // pop an element off the fifo + bool Get( ) ; // discard an element off the fifo + void Flush(); + //void Dump(char *c); + bool FreeSpace(); + bool isEmpty(); + bool isFull(); + void setDebug( bool b); + gavl_video_format_t * getFormat(); + int getSize(); + float getSizePercentage(); + private: + int size ; // Number of elements on FifoVideoFrames + int start ; + int end ; + int count; + gavl_video_frame_t ** fifoPtr ; + gavl_video_format_t * format; + pthread_mutex_t mut; +} ; + + +#endif + diff --git a/src/ReadMedia.cpp b/src/ReadMedia.cpp new file mode 100644 index 0000000..f109da7 --- /dev/null +++ b/src/ReadMedia.cpp @@ -0,0 +1,1132 @@ +#include "ReadMedia.h" +#include +#include //usleep + + +static void *the_thread_dispatcher(void * xp); +static void *the_thread_opener(void * xp); +static void *the_audiofifo_filler(void * xp); +static void *the_videofifo_filler(void * xp); + + +ReadMedia::ReadMedia( ) { + + m_state = STATE_EMPTY; + + m_audio_frame = NULL; + m_video_frame = NULL; + m_aeof = false; + m_veof = false; + m_atime = 0.0; + m_vtime = 0.0; + m_video_stream_count =0; + m_audio_stream_count =0; + + m_pcm_seek = -1; + m_frame_seek = -1; + m_length_in_seconds=0.0; + m_num_frames=0; + m_num_samples=0; + + m_video_format.frame_width=0; + m_video_format.frame_height=0; + m_video_format.image_width=0; + m_video_format.image_height=0; + m_video_format.pixel_width=0; + m_video_format.pixel_height=0; + m_video_format.pixelformat = GAVL_PIXELFORMAT_NONE ; + m_video_format.frame_duration=0; + m_video_format.timescale=0; + m_video_format.framerate_mode=GAVL_FRAMERATE_CONSTANT; + m_video_format.chroma_placement=GAVL_CHROMA_PLACEMENT_DEFAULT; + //m_video_format.interlace_mode=GAVL_INTERLACE_UNKNOWN; + m_video_format.timecode_format.int_framerate =0; + m_video_format.timecode_format.flags =0; + + m_audio_format.samples_per_frame = 0; + m_audio_format.samplerate = 0; + m_audio_format.num_channels = 0; + m_audio_format.sample_format = GAVL_SAMPLE_NONE ; + m_audio_format.interleave_mode = GAVL_INTERLEAVE_NONE; + m_audio_format.center_level = 1.0; + m_audio_format.rear_level = 1.0; + m_audio_format.channel_locations[0] = GAVL_CHID_NONE; // Reset + + + m_fifoaudio= NULL; // new FifoAudioFrames( frames_in_fifo , &tmp_audio_lormat); + m_fifovideo= NULL; // new FifoVideoFrames( 12 , &output_video_format); + m_audio_thread_ret = -1; + m_video_thread_ret = -1; + + m_open_callback = NULL; + m_open_callback_data = NULL; + m_buffer_callback = NULL; + m_buffer_callback_data = NULL; + + quit_av_threads = false; + m_loop = false; + + sprintf(m_filename, "seinettbitte!"); + + //bgav stuff + m_file = NULL; + m_opt = bgav_options_create(); + + bgav_options_set_connect_timeout(m_opt, 5000); + bgav_options_set_read_timeout(m_opt, 5000); + bgav_options_set_network_bandwidth(m_opt, 524300); + bgav_options_set_network_buffer_size(m_opt, 1024*12); + bgav_options_set_http_shoutcast_metadata (m_opt, 1); + // set up the reading so that we can seek sample accurately + bgav_options_set_sample_accurate (m_opt, 1 ); + + + pthread_cond_init(&m_cond_dispatch, 0); + pthread_mutex_init(&m_condmut_dispatch, 0); + + pthread_cond_init(&m_cond_a, 0); + pthread_cond_init(&m_cond_v, 0); + pthread_mutex_init(&m_condmut_a, 0); + pthread_mutex_init(&m_condmut_v, 0); + + pthread_mutex_init(&m_av_mut, 0); + pthread_mutex_init(&m_state_mut, 0); + + // start the dispatcher thread + m_cmd = CMD_START; + m_dispatcher_thread_ret = pthread_create(&m_thread_dispatch, NULL, the_thread_dispatcher, (void *)this) ; + if (m_dispatcher_thread_ret != 0 ) + printf("error starting the readmedia dispatcher thread.\n"); + + while( getCommand() != CMD_NULL) + signalDispatcher(); + //printf("dispatcher ready ...\n"); +} + +ReadMedia::~ReadMedia() { + printf("killing the media..\n"); + setCommand( CMD_QUIT ); + signalDispatcher(); + + + // signal dispatcher joins the opener and AV threads + pthread_join( m_thread_dispatch, NULL); + + //printf("joined dispatcher\n"); + if (m_audio_frame != NULL) { + gavl_audio_frame_destroy(m_audio_frame); + } + if (m_video_frame != NULL) { + gavl_video_frame_destroy(m_video_frame); + } + if (m_file != NULL) { + bgav_close(m_file); + } + + //printf("now, on to deleting fifo...\n"); + if( m_fifoaudio != NULL) delete m_fifoaudio; + if( m_fifovideo != NULL) delete m_fifovideo; + + // these are created only once + bgav_options_destroy(m_opt); + + pthread_cond_destroy(&m_cond_dispatch); + pthread_mutex_destroy(&m_condmut_dispatch); + + pthread_cond_destroy(&m_cond_a); + pthread_cond_destroy(&m_cond_v); + pthread_mutex_destroy(&m_condmut_a); + pthread_mutex_destroy(&m_condmut_v); + + pthread_mutex_destroy(&m_av_mut); + pthread_mutex_destroy(&m_state_mut); + + printf("killed the media..\n"); +} + +int ReadMedia::decodeVideo( gavl_video_frame_t * vf ) { + + // check state first, if state is ready, we can check the other vars without locking + lockState(); + if (m_state != STATE_READY || m_video_stream_count < 1 || m_fifovideo == NULL ) { + unlockState(); + return -1; + } + + if (!m_fifovideo->Get( vf ) ) { + if ( m_veof ) { + unlockState(); + signalV(); + return 0; + } else { + //printf("Couldn't get a video frame, videofifo is %f full\n", m_fifovideo->getSizePercentage()); // this can only happen if the fifo is empty + unlockState(); + signalV(); + return -1; // return with error + } + } + + m_vtime = vf->timestamp / (double)m_video_format.timescale; + unlockState(); + signalV(); + return 1 ; +} + +int ReadMedia::decodeAudio( gavl_audio_frame_t * af ) { + lockState(); + if (m_state != STATE_READY || m_audio_stream_count < 1 || m_fifoaudio == NULL ) { + unlockState(); + return -1; + } + + if ( !m_fifoaudio->Get( af ) ) { + if ( m_aeof ) { + m_pcm_seek = -1; + unlockState(); + signalA(); + return 0; + } else { + //printf("Couldn't get an audio frame, audiofifo is %f full.\n", m_fifoaudio->getSizePercentage()); // this can only happen if the fifo is empty + unlockState(); + signalA(); + return -1; + } + } + + m_atime = af->timestamp / (double)m_audio_format.samplerate; + unlockState(); + signalA(); + return 1 ; +} + +bool ReadMedia::rewind() { + lockState(); + if ( m_state == STATE_READY && m_file != NULL) { + if (m_audio_stream_count){ + //m_audio_frame->valid_samples = 0; + m_pcm_seek = 0; + m_frame_seek = -1; + m_aeof = false; + m_atime = 0; + // need to signal after setting eof to false; + unlockState(); + signalAV(); + lockState(); + } + if ( m_video_stream_count ) { + if (m_audio_stream_count == 0) { + m_frame_seek=0; + m_pcm_seek = -1; + } + m_veof = false; + m_vtime=0; + unlockState(); + signalAV(); + lockState(); + } + unlockState(); + // fifo flush happens when we seek + return true; + } + unlockState(); + return false; +} + +double ReadMedia::getLengthInSeconds() { + double secs = 0.0; + lockState(); + secs = m_length_in_seconds; + unlockState(); + return secs; +} + +int64_t ReadMedia::getLengthInAudioSamples() { + int64_t samples = 0; + lockState(); + samples = m_num_samples; + unlockState(); + return samples; +} + +int64_t ReadMedia::getLengthInVideoFrames() { + int64_t frames = 0; + lockState(); + frames = m_num_frames; + unlockState(); + return frames; +} + +bool ReadMedia::frameSeek( int64_t frames ) { + lockState(); + if (m_state == STATE_READY && m_file && bgav_can_seek( m_file ) && frames >= 0 && frames < m_num_frames ) { + m_frame_seek = frames; + unlockState(); + signalAV(); + return true; + } else { + m_frame_seek = -1; + unlockState(); + return false; + } +} + +// NOT PUBLIC +int64_t ReadMedia::frameSeek() { + int64_t tmp=-1; + lockState(); + tmp = m_frame_seek; + m_frame_seek = -1; + unlockState(); + return tmp; +} + +bool ReadMedia::pcmSeek( int64_t samples ) { + lockState(); + if (m_state == STATE_READY && m_file && bgav_can_seek( m_file) && samples >= 0 && samples < m_num_samples ) { + m_pcm_seek = samples; + unlockState(); + signalAV(); + return true; + } else { + m_pcm_seek = -1; + unlockState(); + return false; + } +} + +// NOT PUBLIC +int64_t ReadMedia::pcmSeek() { + int64_t tmp=-1; + lockState(); + tmp = m_pcm_seek; + m_pcm_seek = -1; + unlockState(); + return tmp; +} + +bool ReadMedia::timeSeek(double seconds) { + gavl_time_t gt = gavl_seconds_to_time( seconds ) ; + + lockState(); + if (m_state == STATE_READY && m_file && bgav_can_seek( m_file) && seconds >= 0.0 && seconds < m_length_in_seconds ) { + if (m_audio_stream_count) { + m_pcm_seek = gavl_time_to_samples(m_audio_format.samplerate, gt ); + if (m_pcm_seek >= m_num_samples || m_pcm_seek < 0) + m_pcm_seek = -1; + unlockState(); + signalAV(); + return true; + } else if ( m_video_stream_count ) { + m_frame_seek = gavl_time_to_frames( m_video_format.timescale, m_video_format.frame_duration, gt ); + if (m_frame_seek >= m_num_frames || m_frame_seek < 0 ) + m_frame_seek = -1; + unlockState(); + signalAV(); + return true; + } + } + unlockState(); + return false; +} + +bool ReadMedia::quitAVThreads() { + bool b =false; + lockState(); + b = quit_av_threads; + unlockState(); + return b; +} + +void ReadMedia::openFile( char * fn, int vsize, int asize, int spf) { + lockState(); + /* + if ( strcmp(m_filename, fn) == 0 && m_state == STATE_READY) { + printf("%s is already open for action. \n", m_filename); + unlockState(); + return; + } + */ + // signal the dispatcher that we want an new file + m_audio_format.samples_per_frame = spf ; + m_afifosize = asize; + m_vfifosize = vsize; + sprintf(m_filename, "%s", fn); + + m_cmd = CMD_OPEN ; + + unlockState(); + signalDispatcher(); +} + +void ReadMedia::copyAudioFormat(gavl_audio_format_t * dst ){ + lockState(); + //if (m_state == STATE_READY) + gavl_audio_format_copy(dst, &m_audio_format); + unlockState(); +} + +void ReadMedia::copyVideoFormat(gavl_video_format_t * dst ){ + lockState(); + //if (m_state == STATE_READY) + gavl_video_format_copy( dst, &m_video_format); + unlockState(); +} + +int ReadMedia::getAudioSamplerate() { + int sr=0; + lockState(); + //if (m_state == STATE_READY ) + sr = m_audio_format.samplerate; + unlockState(); + return sr; +} + +int ReadMedia::getAudioChannelCount() { + int ch=0; + lockState(); + //if (m_state == STATE_READY ) + ch = m_audio_format.num_channels; + unlockState(); + return ch; +} + +int ReadMedia::getVideoTimescale() { + int t=0; + lockState(); + t = m_video_format.timescale; + unlockState(); + return t; +} + +int ReadMedia::getVideoFrameDuration() { + int t=0; + lockState(); + t = m_video_format.frame_duration; + unlockState(); + return t; +} + +char * ReadMedia::getFilename() { + return m_filename; +} + +bgav_t * ReadMedia::getFile() {return m_file;} +FifoAudioFrames * ReadMedia::getAudioFifo() { return m_fifoaudio; } +FifoVideoFrames * ReadMedia::getVideoFifo() { return m_fifovideo; } +gavl_audio_frame_t * ReadMedia::getAudioFrame() { return m_audio_frame;} +gavl_video_frame_t * ReadMedia::getVideoFrame() { return m_video_frame;} + + +void ReadMedia::setState(int b) { + lockState(); + m_state = b; + unlockState(); +} + +int ReadMedia::getState() { + int s=STATE_EMPTY; + lockState(); + s = m_state; + unlockState(); + return s; +} + +bool ReadMedia::isReady() { if ( getState() == STATE_READY) return true; else return false;} +// no need to lock on these +double ReadMedia::getATimeInSeconds() { return m_atime;}; +double ReadMedia::getVTimeInSeconds() { return m_vtime;}; + +float ReadMedia::getTimeInSeconds() { + lockState(); + if (m_audio_stream_count > 0 ) { + unlockState(); + return m_atime; + } else { + // FIXME : see if the following is really true + unlockState(); + return m_vtime; + } +}; + +float ReadMedia::getAudioFifoSizePercentage() { + float f=0.0; + lockState(); + if (m_fifoaudio) + f = m_fifoaudio->getSizePercentage(); + unlockState(); + return f; +} + +void ReadMedia::pealOffVideoFrames( int howmany) { + lockAV(); + if (m_fifovideo == NULL) { + unlockAV(); + return; + } + int max = howmany > m_fifovideo->getSize() ? m_fifovideo->getSize() : howmany; + for (int i=0;i< max; i++) { + m_fifovideo->Get(); + //printf("pealing of a video frame size = %d\n", m_fifovideo->getSize()); + } + unlockAV(); +} + +void ReadMedia::setAEOF(bool b) { + lockState(); + m_aeof = b; + unlockState(); +} +void ReadMedia::setVEOF(bool b) { + lockState(); + m_veof = b; + unlockState(); +} + + +bool ReadMedia::getEOF() { + bool tmp = true; + lockState(); + if (m_state == STATE_READY) + tmp = (m_aeof && m_veof); + unlockState(); + return tmp; +} + +void ReadMedia::setLoop( bool b) { + lockState(); + m_loop = b; + unlockState(); +} +bool ReadMedia::getLoop() { + bool tmp = true; + lockState(); + tmp = m_loop; + unlockState(); + return tmp; +} + +// NOT PUBILC +// only used in fifo filler +bool ReadMedia::getAEOF() { + bool tmp = false; + lockState(); + tmp = m_aeof; + unlockState(); + return tmp; +} + +// NOT PUBLIC +bool ReadMedia::getVEOF() { + bool tmp = false; + lockState(); + tmp = m_veof; + unlockState(); + return tmp; +} + +int ReadMedia::getSamplesPerFrame() { + int spf=0; + lockState(); + spf = m_audio_format.samples_per_frame; + unlockState(); + return spf; +} + +int ReadMedia::lockAV() { + //printf("locking AV\n"); + return pthread_mutex_lock(&m_av_mut); +} +int ReadMedia::unlockAV() { + //printf("unlocking AV\n"); + return pthread_mutex_unlock(&m_av_mut); +} + +int ReadMedia::lockState() { + // printf("locking state.\n"); + return pthread_mutex_lock(&m_state_mut); +} +int ReadMedia::unlockState() { + //printf("unlocking state.\n"); + return pthread_mutex_unlock(&m_state_mut); +} + + +void ReadMedia::waitA() { pthread_cond_wait( &m_cond_a, &m_condmut_a); } +void ReadMedia::waitV() { pthread_cond_wait( &m_cond_v, &m_condmut_v); } +void ReadMedia::signalAV() { signalA(); signalV(); } +void ReadMedia::signalA() { pthread_cond_signal( &m_cond_a); } +void ReadMedia::signalV() { pthread_cond_signal( &m_cond_v); } + +void ReadMedia::signalDispatcher() { pthread_cond_signal( &m_cond_dispatch); } +void ReadMedia::waitDispatch() { pthread_cond_wait( &m_cond_dispatch, &m_condmut_dispatch); } + +void ReadMedia::setOpenCallback(void (*oc)(void *), void *v ) { + lockState();// do we need this? + m_open_callback = oc; + m_open_callback_data = v; + unlockState(); +} +void ReadMedia::callOpenCallback() { + // do NOT lock on the callback, user must do call public functions + // that also lock + if(m_open_callback != NULL) + m_open_callback( m_open_callback_data); + }; + +void ReadMedia::setBufferCallback(bgav_buffer_callback bc, void *v ) { + lockState(); + m_buffer_callback = bc; + m_buffer_callback_data = v; + // set up callbacks. + if (m_buffer_callback) { + bgav_options_set_buffer_callback( m_opt, m_buffer_callback, m_buffer_callback_data); + } + unlockState(); +}; + + + + +int ReadMedia::getAudioStreamCount() { + int asc=0; + lockState(); + asc = m_audio_stream_count; + unlockState(); + return asc; +} +int ReadMedia::getVideoStreamCount() { + int vsc=0; + lockState(); + vsc = m_video_stream_count; + unlockState(); + return vsc; +} + + +// NOT PUBLIC +void ReadMedia::setAudioStreamCount(int s){ + lockState(); + m_audio_stream_count=s; + if (s == 0) // no audio streams, we are already at audio eof + m_aeof = true; + unlockState(); +} +// NOT PUBLIC +void ReadMedia::setVideoStreamCount(int s){ + lockState(); + m_video_stream_count=s; + if (s==0) // if there are no video streams, video is at eof + m_veof = true; + unlockState(); +} + +// NOT PUBLIC +void ReadMedia::setCommand(int s){ + lockState(); + m_cmd = s; + unlockState(); +} + +// NOT PUBLIC +int ReadMedia::getCommand() { + int cmd= CMD_NULL; + lockState(); + cmd = m_cmd; + unlockState(); + return cmd; +} + + +// NOT PUBLIC +// Clears the bgav_t struct, destroying and then creating it +// NO NEED TO DO AV LOCK, only called in the opener thread +void ReadMedia::clearFile() { + if (m_file != NULL) + bgav_close( m_file ); + + m_file = bgav_create(); + bgav_options_copy( bgav_get_options( m_file ) , m_opt); + m_aeof = false; + m_veof = false; + m_pcm_seek = -1; + m_frame_seek = -1; +} + +// NOT PUBLIC +// Closes the bgav_t struct, destroying it and setting to NULL +// NO NEED TO DO AV LOCK, only called in the opener thread +void ReadMedia::closeFile() { + if (m_file != NULL) + bgav_close( m_file ); + m_file = NULL; + m_aeof = false; + m_veof = false; + m_pcm_seek = -1; + m_frame_seek = -1; + + sprintf(m_filename, "seinettbitte!"); +} + + +// NOT PUBLIC +// killAVThreads is only called from the opener thread +// and from the destructor +// THIS IS NOT REALLY A PUBLIC function +void ReadMedia::killAVThreads() { + + lockState(); + m_state = STATE_EMPTY; + quit_av_threads = true; + unlockState(); + + signalAV(); + signalAV(); + + // FIRST need to join the threads that exist and are running + if (m_audio_thread_ret == 0) { + pthread_join( m_thread_fillaudiofifo, NULL); + //pthread_detach( m_thread_fillaudiofifo); + } + if (m_video_thread_ret == 0){ + pthread_join( m_thread_fillvideofifo, NULL); + //pthread_detach( m_thread_fillvideofifo); + } + m_audio_thread_ret = -1; + m_video_thread_ret = -1; + + // no need to lock here + quit_av_threads = false; + +} + +// NOT PUBLIC +// Only called from the opener thread +bool ReadMedia::startAVThreads() { + + if (m_audio_thread_ret == 0 || m_video_thread_ret == 0 ) { + // ouch!, we have running AV threads, this is not good + return false; + } + + if (m_audio_stream_count > 0) { + m_audio_thread_ret = pthread_create(&m_thread_fillaudiofifo, NULL, the_audiofifo_filler, (void *)this); + if (m_audio_thread_ret != 0 ) { + printf("ReadMedia:: problem starting the audio thread\n"); + return false; + } + } + + if (m_video_stream_count > 0) { + m_video_thread_ret = pthread_create(&m_thread_fillvideofifo, NULL, the_videofifo_filler, (void *)this); + if (m_video_thread_ret != 0 ) { + printf("ReadMedia:: problem starting the video thread\n"); + return false; + } + } + return true; +} + +bool ReadMedia::initFormat() { + + const gavl_audio_format_t * open_audio_format; + const gavl_video_format_t * open_video_format; + + // we use the m_vfifosize to see if the user app wants video or not + // then, we set m_video_stream_count to 0 if he doesn't want video + if (m_video_stream_count > 0 && m_vfifosize > 0) { + open_video_format = bgav_get_video_format(m_file, 0); + + if (open_video_format->pixelformat == GAVL_PIXELFORMAT_NONE) { + printf("!!!sorry, pixelformat is not recognized.\n"); + return false; + } + + // let's check to see if the formats are the same, if they are the same + // there is no reason to recreate the fifo or frames + if ( gavl_video_formats_equal( &m_video_format, open_video_format) == 0 ) { + // the formats are different + gavl_video_format_copy (&m_video_format, open_video_format); + if (m_video_frame != NULL) + gavl_video_frame_destroy(m_video_frame); + m_video_frame = gavl_video_frame_create(&m_video_format); + gavl_video_frame_clear( m_video_frame, &m_video_format); + if (m_fifovideo != NULL) + delete m_fifovideo; + m_fifovideo= new FifoVideoFrames( m_vfifosize , &m_video_format); + } + } else { + m_video_stream_count = 0; + } + + // we use the m_afifosize to see if the user app wants audio or not + // then, we set m_audio_stream_count to 0 if he doesn't want audio + if (m_audio_stream_count > 0 && m_afifosize > 0) { + open_audio_format = bgav_get_audio_format(m_file, 0); + + // we can get audio formats that are unkown + if ( open_audio_format->sample_format == GAVL_SAMPLE_NONE) { + printf("sorry, this file has unsupported audio.\n"); + return false; + } + + if ( gavl_audio_formats_equal(&m_audio_format, open_audio_format) == 0 ) { + // audio formats are different + // save the old spf + int spf = m_audio_format.samples_per_frame; + gavl_audio_format_copy(&m_audio_format, open_audio_format); + + if (m_audio_frame != NULL) + gavl_audio_frame_destroy(m_audio_frame); + + // set it back to original + m_audio_format.samples_per_frame = spf ; + + m_audio_frame = gavl_audio_frame_create(&m_audio_format); + gavl_audio_frame_mute( m_audio_frame, &m_audio_format); + if( m_fifoaudio != NULL ) + delete m_fifoaudio; + m_fifoaudio = new FifoAudioFrames( m_afifosize , &m_audio_format); + } + } else { + // user doesn't want audio + m_audio_stream_count = 0; + } + + if (bgav_can_seek(m_file)) { + // set seconds + m_length_in_seconds = gavl_time_to_seconds( bgav_get_duration ( m_file, 0) ); + + // set samples + if (m_audio_stream_count) + if ( bgav_can_seek_sample(m_file) == 1 ) + m_num_samples= bgav_audio_duration ( m_file, 0) ; + else + m_num_samples= gavl_time_to_samples( m_audio_format.samplerate , bgav_get_duration ( m_file, 0) ); + else + m_num_samples=0; + + // set frames + if(m_video_stream_count) + if ( bgav_can_seek_sample(m_file) == 1 ) + m_num_frames = bgav_video_duration ( m_file, 0); + else + m_num_frames = gavl_time_to_frames( m_video_format.timescale, m_video_format.frame_duration , bgav_get_duration ( m_file, 0) ); + else + m_num_frames=0; + + } else { // no can seek; + + m_length_in_seconds = 0.0; + m_num_samples = 0; + m_num_frames = 0; + } + + m_pcm_seek = -1; + m_frame_seek = -1; + + return true; +} + + +void *the_thread_dispatcher(void *xp) { + + ReadMedia *rm = (ReadMedia *)xp; + int cmd = CMD_NULL; + pthread_t thread_open; + int start_thread_ret = -1; + cmd = rm->getCommand(); + + while ( cmd != CMD_QUIT ) { + if (cmd == CMD_OPEN) { + // We already check in the openFile function if the user is trying to open + // a file that is already open. + + // join the opener thread, this will protect again any other calls to open + if (start_thread_ret == 0 ) + pthread_join( thread_open, NULL ); + + // we join the AV threads + // the opener thread will start the AV threads anew upon success + rm->killAVThreads(); + + start_thread_ret = pthread_create(&thread_open, NULL, the_thread_opener, (void *)rm) ; + if (start_thread_ret != 0 ) + printf( "Failed to create m_thread_open thread.\n"); + + } + if (rm->getCommand() == CMD_QUIT) + break; + rm->setCommand( CMD_NULL); + rm->waitDispatch(); + cmd = rm->getCommand(); + } + + //printf("dispatcher: joining thread open\n"); + if( start_thread_ret == 0 ) + pthread_join( thread_open , NULL); + //printf("dispatcher: joined thread open, joining AV's\n"); + rm->killAVThreads(); + //printf("dispatcher: joined AV's\n"); + pthread_exit(NULL); +}; + +void *the_thread_opener(void *xp) { + ReadMedia *rm = NULL; + int num_urls=0, num_tracks=0, audio_stream_count=0, video_stream_count =0; + + rm = (ReadMedia *)xp; + rm->setState(STATE_OPENING); + + // AFTER WE KILL THE THREADS, THERE IS NO NEED TO LOCK AV mutex + // ALL functions that want to get info on the file, seek, + // or decode a frame, MUST first check the state. If the + // state is STATE_READY, then it can perform it's function, + // locking on the necessary variables that might conflict with + // the AV. + + // clearFile deletes old file and creates new File + rm->clearFile(); + + if(!bgav_open(rm->getFile(), rm->getFilename())) { + printf( "Could not open file %s\n", rm->getFilename()); + rm->setState( STATE_EMPTY ); + rm->closeFile(); + rm->callOpenCallback(); + pthread_exit(NULL); + //return NULL; + } else { + printf("opened %s\n", rm->getFilename()); + } + + // check to see if it is a redirector + if(bgav_is_redirector( rm->getFile() )) { + num_urls = bgav_redirector_get_num_urls( rm->getFile() ); + printf( "Found redirector with %d urls inside, we will try to use the first one.\n", num_urls); + printf( "Name %d: %s\n", 1, bgav_redirector_get_name(rm->getFile() , 0)); + printf("URL %d: %s\n", 1, bgav_redirector_get_url(rm->getFile(), 0)); + sprintf(rm->getFilename(), "%s", bgav_redirector_get_url(rm->getFile(), 0) ); + rm->clearFile(); + if (!bgav_open( rm->getFile(), rm->getFilename() )) { + printf("Could not open redirector\n"); + rm->setState( STATE_EMPTY ); + rm->closeFile(); + rm->callOpenCallback(); + pthread_exit(NULL); + //return NULL; + } else { + printf("opened redirector %s\n", rm->getFilename()); + } + } + + num_tracks = bgav_num_tracks(rm->getFile()); + if ( num_tracks ) { + bgav_select_track(rm->getFile(), 0); + } else { + printf("No tracks associated with file:%s\n", rm->getFilename() ); + rm->setState( STATE_EMPTY ); + rm->closeFile(); + rm->callOpenCallback(); + pthread_exit(NULL); + } + + audio_stream_count = bgav_num_audio_streams(rm->getFile(), 0); + if( audio_stream_count ) + bgav_set_audio_stream(rm->getFile(), 0, BGAV_STREAM_DECODE); + + video_stream_count = bgav_num_video_streams(rm->getFile(), 0); + if( video_stream_count ) + bgav_set_video_stream(rm->getFile(), 0, BGAV_STREAM_DECODE); + + rm->setVideoStreamCount(video_stream_count); + rm->setAudioStreamCount(audio_stream_count); + + if(!bgav_start(rm->getFile())) { + printf( "failed to start file\n"); + rm->setState( STATE_EMPTY ); + rm->closeFile(); + rm->callOpenCallback(); + pthread_exit(NULL) ; + //return NULL; + } + + if( !rm->initFormat() ){ + rm->setState( STATE_EMPTY ); + rm->closeFile(); + rm->callOpenCallback(); + pthread_exit(NULL) ; + } + + if( !rm->startAVThreads() ){ + rm->setState( STATE_EMPTY ); + rm->closeFile(); + rm->callOpenCallback(); + pthread_exit(NULL) ; + } + + // AV threads are now running, blocking will be necessary + // STATE_READY and callOpenCallback is set/called in the + // fifo fill callbacks + rm->signalAV(); + rm->signalAV(); //extra signal for second thread + pthread_exit(NULL); + //return NULL; +} + +void *the_audiofifo_filler( void * xp) { + int samples_returned=0; + ReadMedia *rm = (ReadMedia *)xp; + int first = true; + int dovideo = rm->getVideoStreamCount(); + int spf = rm->getSamplesPerFrame(); + int samplerate = rm->getAudioSamplerate(); + int64_t seekto =-1; + int can_seek = bgav_can_seek ( rm->getFile() ); + int can_seek_sample = bgav_can_seek_sample ( rm->getFile() ); + + while (!rm->quitAVThreads() ) { + + //while ( rm->getAudioFifo() != NULL && rm->getAudioFifo()->FreeSpace() && !rm->getAEOF() ) { + while ( rm->getAudioFifo()->FreeSpace() && !rm->getAEOF() ) { + + if (rm->quitAVThreads() ) pthread_exit(NULL) ; //return NULL; + + rm->lockAV(); + // check to see if we need to seek + // if this is set, we already know we can seek on this file + // and don't need to check with bgav_can_seek + if ( can_seek && (seekto = rm->pcmSeek() ) >= 0 ) { + rm->getAudioFifo()->Flush(); + if (dovideo && rm->getVideoFifo() ) rm->getVideoFifo()->Flush(); + if ( can_seek_sample ) { + bgav_seek_audio(rm->getFile() , 0, seekto ); + } else { + gavl_time_t gt = gavl_samples_to_time( samplerate, seekto ) ; + bgav_seek(rm->getFile(), >); + } + } + + samples_returned = bgav_read_audio(rm->getFile(), rm->getAudioFrame(), 0, spf ); + //rm->unlockAV(); + + if (samples_returned == 0 ) { + if( rm->getLoop() ) { + if ( can_seek ) { + // Now, rewind the file, don't flush the fifo's + if (can_seek_sample) { + bgav_seek_audio(rm->getFile() , 0, 0); + } else { + gavl_time_t gt = 0; + bgav_seek(rm->getFile(), >); + } + // SAVE THIS FOR ANOTHER TIME, OVERLAPPED SMOOTH LOOPING + //if ( rm->getLoop() > 1 && bgav_read_audio(rm->getFile(), rm->getAudioFrame(), 0, spf ) ) { + // add to the fifo, overlapping + // rm->getAudioFifo()->AppendOverlap( rm->getAudioFrame(), 5 ); + // } + + + } else { // this file is not seekable, what do we do? + printf("cannot seek on file, but we want to loop. setting end of file.\n"); + rm->setAEOF(true); + rm->pcmSeek();// clear the seek var just in case + } + } else { + rm->setAEOF(true); + rm->pcmSeek(); // clear the seek var just in case + } + rm->unlockAV(); + break; + } + rm->unlockAV(); + if( !rm->getAudioFifo()->Append( rm->getAudioFrame() )) + printf("problem with appending Audio Frame\n"); + } + if (first && !dovideo) { + rm->setState( STATE_READY ); + rm->callOpenCallback(); + first = false; + } + if (rm->quitAVThreads() ) pthread_exit(NULL); //return NULL; + rm->waitA(); + } + pthread_exit(NULL);//return NULL; +} + +void *the_videofifo_filler( void * xp) { + ReadMedia *rm = (ReadMedia *)xp; + int ret = 0; + int first = true; + int doaudio = rm->getAudioStreamCount(); + int64_t seekto =-1; + int can_seek = bgav_can_seek ( rm->getFile() ); + int can_seek_sample = bgav_can_seek_sample ( rm->getFile() ); + int timescale = rm->getVideoTimescale(); + int frame_duration = rm->getVideoFrameDuration(); + + while (!rm->quitAVThreads() ) { + + while ( rm->getVideoFifo() !=NULL && rm->getVideoFifo()->FreeSpace() && !rm->getVEOF() ) { + + if (rm->quitAVThreads() ) pthread_exit(NULL);//return NULL; + + rm->lockAV(); + // check to see if we need to seek + // if this is set, we already know we can seek on this file + // and don't need to check with bgav_can_seek + if ( (seekto = rm->frameSeek() ) >= 0 ) { + if (doaudio && rm->getAudioFifo()) rm->getAudioFifo()->Flush(); + rm->getVideoFifo()->Flush(); + gavl_time_t gt = gavl_frames_to_time (timescale, frame_duration, seekto ); + if ( can_seek_sample ) { + bgav_seek_video(rm->getFile() , 0, gt ); + } else { + bgav_seek(rm->getFile(), >); + } + } + + ret = bgav_read_video(rm->getFile(), rm->getVideoFrame(), 0 ); + + if ( !ret ) { + // only loop from video if there is no audio + // audio controls loop timing + if ( !doaudio && rm->getLoop() ) { + + if ( can_seek ) { + // Now, rewind the file, don't flush fifos + if (can_seek_sample) { + bgav_seek_video(rm->getFile() , 0, 0); + } else { + gavl_time_t gt = 0; + bgav_seek(rm->getFile(), >); + } + } else { // this file is not seekable, what do we do? + printf("cannot seek on file, but we want to loop. setting end of file.\n"); + rm->setVEOF(true); + //rm->frameSeek();// clear the seek var just in case + } + + } else { + rm->setVEOF(true); + } + rm->unlockAV(); + break; + } + rm->unlockAV(); + if( !rm->getVideoFifo()->Append( rm->getVideoFrame() )) + printf("problem with appending VideoFrame\n"); + } + // on the first time 'round we will call the open callback + // if there is no video in the file, the audio will handle it. + if (first) { + rm->setState( STATE_READY ); + rm->callOpenCallback(); + first = false; + } + + if (rm->quitAVThreads() ) pthread_exit(NULL); //return NULL; + rm->waitV(); + } + pthread_exit(NULL); //return NULL; +} + + diff --git a/src/ReadMedia.h b/src/ReadMedia.h new file mode 100644 index 0000000..d0a7627 --- /dev/null +++ b/src/ReadMedia.h @@ -0,0 +1,220 @@ +#ifndef _READMEDIA_H_ +#define _READMEDIA_H_ + +#include +#include +#include +#include "FifoAudioFrames.h" +#include "FifoVideoFrames.h" + + +#ifndef _AVDEC_H_ +#define _AVDEC_H_ +extern "C" { +#include +} +#endif + + +#define STATE_EMPTY 0 +#define STATE_OPENING 1 +#define STATE_READY 2 + +#define SRC_MAX 256.0 +#define SRC_MIN 1/256.0 + +#define CMD_START 3 +#define CMD_NULL 0 +#define CMD_OPEN 1 +#define CMD_QUIT 2 + +class ReadMedia { + + public: + ReadMedia(); + ~ReadMedia(); + + void openFile( char * filename, int vfifosize, int afifosize, int samples_per_frame); + + int decodeAudio( gavl_audio_frame_t *af); + int decodeVideo( gavl_video_frame_t *vf); + + bool isReady(); // see if file is loaded or not + bool getEOF(); + + bool rewind(); + bool rewindNoFlush(); + bool frameSeek(int64_t frames); + bool pcmSeek(int64_t samples); + bool timeSeek(double seconds); + + int getAudioSamplerate(); + int getAudioChannelCount(); + int getVideoTimescale(); + int getVideoFrameDuration(); + + double getLengthInSeconds(); + int64_t getLengthInAudioSamples(); + int64_t getLengthInVideoFrames(); + + int getAudioStreamCount(); + int getVideoStreamCount(); + + // this is used to sync AV frames + void pealOffVideoFrames(int howmany); + + void dump() { lockState(); if (m_file != NULL) bgav_dump(m_file); unlockState(); }; + + double getATimeInSeconds(); + double getVTimeInSeconds(); + float getTimeInSeconds(); + float getAudioFifoSizePercentage(); + + void copyAudioFormat(gavl_audio_format_t * dst ); + void copyVideoFormat(gavl_video_format_t * dst ); + void setLoop( bool b); + bool getLoop(); + + + //||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + // NON-PUBLIC public functions + //||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + + int64_t pcmSeek(); + int64_t frameSeek(); + //bool seek(gavl_time_t gt); + + void killAVThreads(); + bool startAVThreads(); + void setAudioStreamCount(int s); + void setVideoStreamCount(int s); + + FifoAudioFrames * getAudioFifo(); + FifoVideoFrames * getVideoFifo(); + + bgav_t * getFile(); + char * getFilename(); + + gavl_audio_frame_t * getAudioFrame(); + gavl_video_frame_t * getVideoFrame(); + + void closeFile(); + void clearFile(); + + void setState(int b); + int getState(); + gavl_audio_format_t * getAudioFormat() { return &m_audio_format;}; + gavl_video_format_t * getVideoFormat() { return &m_video_format;}; + + void setCommand( int c); + int getCommand(); + + void setAEOF(bool b); + void setVEOF(bool b); + bool getAEOF(); + bool getVEOF(); + + int getSamplesPerFrame(); + + int lockAV(); + int unlockAV(); + //int lockAV(const char *s); + //int unlockAV(const char *s); + int lockState(); + int unlockState(); + //int lockState(const char *s); + //int unlockState(const char *s); + + + void waitA(); + void waitV(); + void signalAV(); + void signalA(); + void signalV(); + void waitDispatch(); + void signalDispatcher(); + + bool initFormat(); + + void setOpenCallback(void (*oc)(void *), void *v ); + void callOpenCallback(); + + void setBufferCallback( bgav_buffer_callback bc, void *v ); + + bool quitAVThreads(); + + private: + bool quit_av_threads; + // callbacks for open, input buffer etc. + void * m_open_callback_data; + void (* m_open_callback)(void * v); + bgav_buffer_callback m_buffer_callback; + void * m_buffer_callback_data; + + // end of file for audio and video + bool m_aeof; + bool m_veof; + + // seek vars + int64_t m_pcm_seek; + int64_t m_frame_seek; + + double m_length_in_seconds; + int64_t m_num_samples; + int64_t m_num_frames; + + // command for dispatcher thread + int m_cmd; + + // internal state of media reader and + // current filename + int m_state; + char m_filename[1024]; + bool m_loop; + + + // current time of audio in seconds + double m_atime; + double m_vtime; + + int m_afifosize; + int m_vfifosize; + int m_audio_stream_count; + int m_video_stream_count; + + bgav_t * m_file; + bgav_options_t * m_opt; + + // audio stuff + gavl_audio_frame_t * m_audio_frame; + gavl_audio_format_t m_audio_format; + + //video stuff + gavl_video_frame_t * m_video_frame; + gavl_video_format_t m_video_format; + + FifoAudioFrames *m_fifoaudio; + FifoVideoFrames *m_fifovideo; + + int m_audio_thread_ret; + int m_video_thread_ret; + int m_dispatcher_thread_ret; + + pthread_t m_thread_fillaudiofifo; + pthread_t m_thread_fillvideofifo; + pthread_t m_thread_dispatch; + + pthread_mutex_t m_condmut_a; + pthread_mutex_t m_condmut_v; + pthread_mutex_t m_condmut_dispatch; + + pthread_mutex_t m_state_mut; + pthread_mutex_t m_av_mut; + + pthread_cond_t m_cond_a; + pthread_cond_t m_cond_v; + pthread_cond_t m_cond_dispatch; + +}; + +#endif diff --git a/src/readanysf~.cpp b/src/readanysf~.cpp new file mode 100644 index 0000000..811ddef --- /dev/null +++ b/src/readanysf~.cpp @@ -0,0 +1,507 @@ +/* + * Readanysf PD object for reading and playing multiple soundfile types + * from disk and from the web using gmerlin_avdecode + * + * Copyright (C) 2003-2010 August Black + * + * 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 + * (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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * readanysf.cpp + */ + +#include +#include +#include +#include +#include //IMPORTANT bool +#include // for malloc +#include //sprintf +#include //strcmp +#include // ceil + +#include "m_pd.h" +#include "ReadMedia.h" + +#define MAXSFCHANS 64 // got this from d_soundfile.c in pd/src + + +static t_class *readanysf_class; + +typedef struct readanysf { + t_object x_obj; + t_sample *(x_outvec[MAXSFCHANS]); + t_outlet *outinfo; + + int blocksize; // size of the pd block for this object + int num_channels; + int num_frames_in_fifo; + int num_samples_per_frame; + unsigned int tick; // how often to send outlet info + bool play; + bool is_opening; + unsigned int count; + float src_factor; + + bool do_t2o_audio_convert; + bool do_i2t_audio_convert; + + int samplesleft; // how many audio samples left in our curren read buf + + + ReadMedia *rm; + gavl_audio_frame_t * out_audio_frame; + gavl_audio_frame_t * tmp_audio_frame; + gavl_audio_frame_t * in_audio_frame; + + gavl_audio_format_t out_audio_format; + gavl_audio_format_t tmp_audio_format; + gavl_audio_format_t in_audio_format; + + gavl_audio_converter_t * i2t_audio_converter; + gavl_audio_converter_t * t2o_audio_converter; + + pthread_mutex_t mut; + +} t_readanysf; + + +void m_play(t_readanysf *x) { + + pthread_mutex_lock(&x->mut); + if (x->rm->isReady() && !x->is_opening ) { + // is_opening protects the play variable, which in turn protects + // the memory accessed in m_decode_block inside of the perform function + // as long as play is false, no memory is accessed + // as long as is_opening is true, play is false and no memory is accessed + // to be safe, we will protect the is_opening var so that when the open callback + // is called, and the memory is de-allocated and re-allocated, that memory is not + // access by the perform function + x->play = true; // this is the only place where play is true + } else { + if (x->is_opening ) { + post("Current file is still starting."); + post("This probably means that it is a stream and it needs to buffer in from the network."); + } else { + post("Current file is either invalid or an unsupported codec."); + } + } + pthread_mutex_unlock(&x->mut); +} + +void readanysf_bang(t_readanysf *x) { + m_play(x); +} + + +void m_pause(t_readanysf *x) { + x->play = false; +} + +void m_pcm_seek(t_readanysf *x, float f) { + if (! x->rm->pcmSeek( (long)f) ) + post("can't seek on this file."); +} + +void m_time_seek(t_readanysf *x, float f) { + if (! x->rm->timeSeek( (double)f) ) + post("can't seek on this file."); +} + +void m_tick(t_readanysf *x, float f) { + if (f >= 0.0) { + x->tick = (unsigned int) f ; + } +} + +void m_stop(t_readanysf *x) { + x->play = false; + x->rm->rewind(); +} + +void m_init_audio( t_readanysf *x) { + // Now,. do Audio stuff + x->rm->copyAudioFormat( &x->in_audio_format); + x->in_audio_format.samples_per_frame = x->num_samples_per_frame; + + x->tmp_audio_format.samplerate = x->in_audio_format.samplerate; + x->tmp_audio_format.samples_per_frame = x->in_audio_format.samples_per_frame; + x->tmp_audio_format.num_channels = x->out_audio_format.num_channels; + x->tmp_audio_format.channel_locations[0] = GAVL_CHID_NONE; // Reset + gavl_set_channel_setup (&x->tmp_audio_format); // Set channel locations + + if (x->in_audio_frame != NULL) + gavl_audio_frame_destroy(x->in_audio_frame); + x->in_audio_frame = gavl_audio_frame_create(&x->in_audio_format); + + if (x->tmp_audio_frame != NULL) + gavl_audio_frame_destroy(x->tmp_audio_frame); + x->tmp_audio_frame = gavl_audio_frame_create(&x->tmp_audio_format); + + /* m_out_audio_format.samples_per_frame = (m_out_audio_format.samplerate / (double)m_get_audio_format.samplerate) * + m_get_audio_format.samples_per_frame + 10; + */ + + if (x->i2t_audio_converter == NULL) + x->i2t_audio_converter = gavl_audio_converter_create( ); + x->do_i2t_audio_convert = gavl_audio_converter_init( x->i2t_audio_converter, &x->in_audio_format, &x->tmp_audio_format); + + if (x->t2o_audio_converter == NULL) + x->t2o_audio_converter = gavl_audio_converter_create( ); + x->do_t2o_audio_convert = gavl_audio_converter_init_resample( x->t2o_audio_converter, &x->out_audio_format); + + // FIXME: this should be protected + x->src_factor = x->out_audio_format.samplerate / (float) x->in_audio_format.samplerate; + /* + printf("in audio format: \n"); + gavl_audio_format_dump( &x->in_audio_format); + printf("tmp audio format, converting from input=%d \n", x->do_i2t_audio_convert); + gavl_audio_format_dump( &x->tmp_audio_format); + printf("output audio format, converting from tmp=%d \n", x->do_t2o_audio_convert); + gavl_audio_format_dump( &x->out_audio_format); + */ +}; + + +void m_open_callback( void * data) { + t_atom lst; + t_readanysf * x = (t_readanysf *)data; + + pthread_mutex_lock(&x->mut); + x->is_opening = true; // set it here again just to be safe + + if (x->rm->isReady() && x->rm->getAudioStreamCount() ) { + + m_init_audio(x); + + // FIXME: is it safe to call these here? + SETFLOAT(&lst, (float)x->rm->getAudioSamplerate() ); + outlet_anything(x->outinfo, gensym("samplerate"), 1, &lst); + + SETFLOAT(&lst, x->rm->getLengthInSeconds() ); + outlet_anything(x->outinfo, gensym("length"), 1, &lst); + + outlet_float(x->outinfo, 0.0); + + // ready should be last + SETFLOAT(&lst, 1.0 ); + outlet_anything(x->outinfo, gensym("ready"), 1, &lst); + // set time to 0 again here just to be sure + } else { + SETFLOAT(&lst, 0.0 ); + outlet_anything(x->outinfo, gensym("samplerate"), 1, &lst); + SETFLOAT(&lst, 0.0 ); + outlet_anything(x->outinfo, gensym("length"), 1, &lst); + SETFLOAT(&lst, 0.0 ); + outlet_anything(x->outinfo, gensym("ready"), 1, &lst); + outlet_float(x->outinfo, 0.0); + post("Invalid file or unsupported codec."); + } + x->is_opening=false; + pthread_mutex_unlock(&x->mut); + +} + +void m_open(t_readanysf *x, t_symbol *s) { + + t_atom lst; + SETFLOAT(&lst, 0.0 ); + outlet_anything(x->outinfo, gensym("ready"), 1, &lst); + + SETFLOAT(&lst, 0.0 ); + outlet_anything(x->outinfo, gensym("length"), 1, &lst); + + outlet_float(x->outinfo, 0.0); + + x->play = false; + pthread_mutex_lock(&x->mut); + x->is_opening = true; + pthread_mutex_unlock(&x->mut); + x->rm->openFile( s->s_name, 0, x->num_frames_in_fifo, x->num_samples_per_frame ); +} + +void m_speed(t_readanysf *x, float f) { + // x->rm->setSpeed( f ); + if (f > SRC_MAX) + return; + if (f < SRC_MIN) + return; + // lock on src_factor because it is set called during open callback + // we can be sure then it won't conflict + pthread_mutex_lock(&x->mut); + x->src_factor = 1.0/f; + pthread_mutex_unlock(&x->mut); +} + +void m_loop(t_readanysf *x, float f) { + if ( f == 0) + x->rm->setLoop( false ); + else + x->rm->setLoop( true ); + post("looping = %d", x->rm->getLoop()); +} + + +static void *readanysf_new(t_float f, t_float f2, t_float f3 ) { + + int nchannels = (int)f; + int nframes = (int)f2; + int nsamples = (int)f3; + int i; + t_atom lst; + + // if the external is created without any options + if (nchannels <=0) + nchannels = 2; + + if (nframes <=0) + nframes = 24; + + if (nsamples <=0) + nsamples = sys_getblksize(); + + t_readanysf *x = (t_readanysf *)pd_new(readanysf_class); + x->blocksize=0; + x->num_channels = nchannels; + x->num_frames_in_fifo = nframes; + x->num_samples_per_frame = nsamples; + x->tick = 1000; + x->play =false; + x->is_opening=false; + x->count = 0; + x->src_factor = 1.0; + x->do_t2o_audio_convert = false; + x->do_i2t_audio_convert = false; + x->samplesleft = 0; + + x->rm = NULL; + + x->out_audio_frame=NULL; + x->tmp_audio_frame=NULL; + x->in_audio_frame=NULL; + + // set up the audio formats in dsp call + + x->i2t_audio_converter=NULL; + x->t2o_audio_converter=NULL; + + pthread_mutex_init(&x->mut, 0); + + for (i=0; i < nchannels; i++) { + outlet_new(&x->x_obj, gensym("signal")); + } + x->outinfo = outlet_new(&x->x_obj, &s_anything); + SETFLOAT(&lst, 0.0 ); + outlet_anything(x->outinfo, gensym("ready"), 1, &lst); + + // set time to 0.0 + outlet_float(x->outinfo, 0.0); + if (x->rm == NULL) { + x->rm = new ReadMedia ( ); // (int)sys_getsr(), x->num_channels, x->num_frames_in_fifo, x->num_samples_per_frame); + post("Created new readanysf~ with %d channels and internal buffer of %d * %d = %d", x->num_channels, + x->num_frames_in_fifo, x->num_samples_per_frame, x->num_frames_in_fifo * x->num_samples_per_frame); + } + x->rm->setOpenCallback( m_open_callback, (void *)x); + + return (void *)x; +} + +int m_get_frame( t_readanysf *x ) { + int ret =0; + ret = x->rm->decodeAudio(x->in_audio_frame); + if (ret != 1) // EOF + return ret; + + if (x->do_i2t_audio_convert) { + gavl_audio_convert( x->i2t_audio_converter, x->in_audio_frame, x->tmp_audio_frame) ; + } else { + gavl_audio_frame_copy(&x->in_audio_format, x->tmp_audio_frame, x->in_audio_frame, + 0,0, x->in_audio_frame->valid_samples, x->in_audio_frame->valid_samples) ; + x->tmp_audio_frame->valid_samples = x->in_audio_frame->valid_samples; + } + + if ( x->do_t2o_audio_convert ) { // should be true all of the time + //gavl_audio_convert( t2o_audio_converter, taf, oaf ); + gavl_audio_converter_resample( x->t2o_audio_converter, x->tmp_audio_frame, x->out_audio_frame, x->src_factor ); + // Don't know why, but on the first conversion, I get one extra sample + // THIS SHOULD NOT HAPPEN...this is a fix for now..check it out later. + //if (x->src_factor == 1.0 && x->out_audio_frame->valid_samples > x->num_samples_per_frame) { + // printf("Got wierd return value for audio frames, taf->vs %d, oaf->vs %d, src_factor=%f\n", + // x->tmp_audio_frame->valid_samples, x->out_audio_frame->valid_samples, x->src_factor); + //x->samplesleft = x->out_audio_frame->valid_samples = x->num_samples_per_frame; + //} else { + x->samplesleft = x->out_audio_frame->valid_samples; + //} + } else { + // copy the samples to the output + gavl_audio_frame_copy(&x->tmp_audio_format, x->out_audio_frame, x->tmp_audio_frame, + 0,0, x->tmp_audio_frame->valid_samples, x->tmp_audio_frame->valid_samples) ; + //printf("copying taf to oaf, taf->vs %d, oaf->vs %d\n", taf->valid_samples, oaf->valid_samples); + x->samplesleft = x->tmp_audio_frame->valid_samples; + x->out_audio_frame->valid_samples = x->tmp_audio_frame->valid_samples; + } + return ret; +} + +int m_decode_block( t_readanysf * x ) { + int i=0,j=0, samps_done=0; + int samps_to_do = x->blocksize; + + while( samps_to_do > 0) { + if ( samps_to_do <= x->samplesleft) { + //if (x->out_audio_frame->valid_samples < x->samplesleft) + // printf("error\n"); + // copy our samples out to the pd audio buffer + for (i = 0; i < x->num_channels; i++) { + for (j = 0; j < samps_to_do ; j++) { + x->x_outvec[i][samps_done + j] = x->out_audio_frame->channels.f[i][ x->out_audio_frame->valid_samples - x->samplesleft +j ]; + } + } + x->samplesleft -= samps_to_do; + samps_done += samps_to_do; + samps_to_do = 0; + break; + } else if ( x->samplesleft > 0 ) { + //if( x->out_audio_frame->valid_samples < x->samplesleft) + // printf("valid_samples < samplesleft, shouldn't happen\n"); + for (i = 0; i < x->num_channels; i++) { + for (j = 0; j < x->samplesleft; j++) { + x->x_outvec[i][samps_done + j] = x->out_audio_frame->channels.f[i][ x->out_audio_frame->valid_samples - x->samplesleft +j ]; + } + } + samps_to_do = samps_to_do - x->samplesleft; + samps_done += x->samplesleft; + x->samplesleft = 0; + } else { // samplesleft is zero + int ret = m_get_frame(x); + if (ret == 0) { + return samps_done; + } else if (ret == -1) { + //printf("error getting frame...must be seeking\n"); + return ret; + } + } + } + return samps_done; +} + +static t_int *readanysf_perform(t_int *w) { + t_readanysf *x = (t_readanysf *) (w[1]); + int i=0,j=0; + int samples_returned = 0; + t_atom lst; + + if (x->play ) { // play protects the memory accessed in m_decode_block + samples_returned = m_decode_block( x ); + if (samples_returned == 0 ) { // EOF + m_stop(x); + outlet_bang(x->outinfo); + } else if (samples_returned == -1) { + // error in getting audio, normally from seeking + samples_returned=0; + } + } + + for (i = 0; i < x->num_channels; i++) { + for (j = samples_returned; j < x->blocksize; j++) { + x->x_outvec[i][j] = 0.0; + } + } + + // just set some variables + if ( ++x->count > x->tick ) { + SETFLOAT (&lst, x->rm->getAudioFifoSizePercentage() ); + outlet_anything(x->outinfo, gensym("cache"), 1, &lst); + if (x->play) { + outlet_float(x->outinfo, x->rm->getTimeInSeconds()); + } + x->count = 0; + } + + return (w+2); +} + +void readanysf_dsp(t_readanysf *x, t_signal **sp) { + int i=0; + + if (x->blocksize != sp[0]->s_n) { + x->blocksize = sp[0]->s_n; + + x->tmp_audio_format.samplerate = sys_getsr(); + x->tmp_audio_format.sample_format = GAVL_SAMPLE_FLOAT ; + x->tmp_audio_format.interleave_mode = GAVL_INTERLEAVE_NONE; + x->tmp_audio_format.num_channels = x->num_channels; + x->tmp_audio_format.channel_locations[0] = GAVL_CHID_NONE; // Reset + x->tmp_audio_format.samples_per_frame = x->num_samples_per_frame; + + x->out_audio_format.samplerate = sys_getsr(); + x->out_audio_format.sample_format = GAVL_SAMPLE_FLOAT ; + x->out_audio_format.interleave_mode = GAVL_INTERLEAVE_NONE; + x->out_audio_format.num_channels = x->num_channels; + x->out_audio_format.channel_locations[0] = GAVL_CHID_NONE; // Reset + + // leave enough room in our out format and frame for resampling + x->out_audio_format.samples_per_frame = x->num_samples_per_frame * SRC_MAX +10; + gavl_set_channel_setup (&x->out_audio_format); // Set channel locations + + if(x->out_audio_frame != NULL) + gavl_audio_frame_destroy( x->out_audio_frame); + x->out_audio_frame = gavl_audio_frame_create(&x->out_audio_format); + printf("created new out frame in readanysf_dsp\n"); + post("pd blocksize=%d, spf=%d", x->blocksize, x->num_samples_per_frame); + } + + for (i = 0; i < x->num_channels; i++) + x->x_outvec[i] = sp[i]->s_vec; + + dsp_add(readanysf_perform, 1, x); +} + +static void readanysf_free(t_readanysf *x) { + // delete the readany objs + + if (x->in_audio_frame != NULL) gavl_audio_frame_destroy(x->in_audio_frame); + if (x->tmp_audio_frame != NULL) gavl_audio_frame_destroy(x->tmp_audio_frame); + if (x->out_audio_frame != NULL) gavl_audio_frame_destroy(x->out_audio_frame); + + if (x->i2t_audio_converter != NULL) + gavl_audio_converter_destroy(x->i2t_audio_converter); + + if (x->t2o_audio_converter != NULL) + gavl_audio_converter_destroy(x->t2o_audio_converter); + + pthread_mutex_destroy(&x->mut); + + delete x->rm; + x->rm = NULL; +} + +extern "C" void readanysf_tilde_setup(void) { + + readanysf_class = class_new(gensym("readanysf~"), (t_newmethod)readanysf_new, + (t_method)readanysf_free, sizeof(t_readanysf), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + + class_addmethod(readanysf_class, (t_method)readanysf_dsp, gensym("dsp"), A_NULL); + class_addmethod(readanysf_class, (t_method)m_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(readanysf_class, (t_method)m_play, gensym("play"), A_NULL); + class_addmethod(readanysf_class, (t_method)m_pause, gensym("pause"), A_NULL); + class_addmethod(readanysf_class, (t_method)m_stop, gensym("stop"), A_NULL); + class_addmethod(readanysf_class, (t_method)m_tick, gensym("tick"), A_FLOAT, A_NULL); + class_addmethod(readanysf_class, (t_method)m_speed, gensym("speed"), A_FLOAT, A_NULL); + class_addmethod(readanysf_class, (t_method)m_loop, gensym("loop"), A_FLOAT, A_NULL); + class_addmethod(readanysf_class, (t_method)m_pcm_seek, gensym("pcm_seek"), A_FLOAT, A_NULL); + class_addmethod(readanysf_class, (t_method)m_time_seek, gensym("time_seek"), A_FLOAT, A_NULL); + class_addbang(readanysf_class, readanysf_bang); + +} diff --git a/stress.pd b/stress.pd new file mode 100644 index 0000000..de529ed --- /dev/null +++ b/stress.pd @@ -0,0 +1,53 @@ +#N canvas 525 43 611 566 10; +#X obj 92 457 readanysf~; +#X obj 89 167 * -1; +#X obj 89 190 sel -1 1; +#X obj 89 142 f 1; +#X obj 89 54 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 +-1; +#X msg 176 53 0; +#X obj 92 491 dac~; +#X obj 184 265 del 50; +#X obj 469 307 del 50; +#X msg 184 285 play; +#X msg 469 328 play; +#X floatatom 161 192 7 0 0 0 - - -; +#X obj 161 142 f; +#X obj 187 142 + 1; +#X obj 89 98 metro 100; +#X obj 76 299 del 5; +#X obj 380 306 del 5; +#X msg 345 306 stop; +#X msg 33 306 stop; +#X msg 208 428 open /home/august/mp3/ygr_banhart/banhart_blackbabies_01.wma +; +#X msg 75 319 open /home/august/mp3/lauriealan0.mp3; +#X connect 0 0 6 0; +#X connect 0 1 6 1; +#X connect 1 0 2 0; +#X connect 1 0 3 1; +#X connect 2 0 18 0; +#X connect 2 0 7 0; +#X connect 2 0 15 0; +#X connect 2 1 17 0; +#X connect 2 1 8 0; +#X connect 2 1 16 0; +#X connect 3 0 1 0; +#X connect 4 0 14 0; +#X connect 5 0 12 1; +#X connect 5 0 14 0; +#X connect 7 0 9 0; +#X connect 8 0 10 0; +#X connect 9 0 0 0; +#X connect 10 0 0 0; +#X connect 12 0 13 0; +#X connect 12 0 11 0; +#X connect 13 0 12 1; +#X connect 14 0 3 0; +#X connect 14 0 12 0; +#X connect 15 0 20 0; +#X connect 16 0 19 0; +#X connect 17 0 0 0; +#X connect 18 0 0 0; +#X connect 19 0 0 0; +#X connect 20 0 0 0; -- cgit v1.2.1