@@ -117,7 +117,7 @@ Audio_Track::handle ( int m ) | |||
/* THREAD: IO */ | |||
/** determine region coverage and fill /buf/ with interleaved samples | |||
* from /frame/ to /nframes/ for exactly /channels/ channels. */ | |||
void | |||
nframes_t | |||
Audio_Track::play ( sample_t *buf, nframes_t frame, nframes_t nframes, int channels ) | |||
{ | |||
sample_t *cbuf = new sample_t[ nframes ]; | |||
@@ -125,9 +125,9 @@ Audio_Track::play ( sample_t *buf, nframes_t frame, nframes_t nframes, int chann | |||
/* quick and dirty--let the regions figure out coverage for themselves */ | |||
for ( list <Track_Widget *>::const_iterator i = _widgets.begin(); i != _widgets.end(); i++ ) | |||
{ | |||
const Region *r = (Region*)i; | |||
const Region *r = (Region*)(*i); | |||
for ( int i = channels; i-- ) | |||
for ( int i = channels; i--; ) | |||
{ | |||
memset( cbuf, 0, nframes * sizeof( sample_t ) ); | |||
@@ -136,8 +136,11 @@ Audio_Track::play ( sample_t *buf, nframes_t frame, nframes_t nframes, int chann | |||
/* interleave */ | |||
int k = 0; | |||
for ( int j = 0; j < nframes; j += channels ) | |||
for ( unsigned int j = 0; j < nframes; j += channels ) | |||
buf[ j ] = cbuf[ k++ ]; | |||
} | |||
} | |||
/* FIXME: bogus */ | |||
return nframes; | |||
} |
@@ -22,6 +22,7 @@ | |||
#include "Track.H" | |||
#include "Region.H" | |||
#include <FL/Fl_Input.H> | |||
class Audio_Track : public Track | |||
@@ -53,6 +54,6 @@ public: | |||
void dump ( void ); | |||
void remove_selected ( void ); | |||
void play ( sample_t *buf, nframes_t frame, nframes_t nframes, int channels ); | |||
nframes_t play ( sample_t *buf, nframes_t frame, nframes_t nframes, int channels ); | |||
}; |
@@ -17,9 +17,12 @@ | |||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |||
/*******************************************************************************/ | |||
#include "Disk_Stream.H" | |||
#include "Track_Header.H" | |||
#include "Audio_Track.H" | |||
#include "Port.H" | |||
static float seconds_to_buffer = 5.0f; | |||
float Disk_Stream::seconds_to_buffer = 5.0f; | |||
/* A Disk_Stream uses a separate I/O thread to stream a track's | |||
regions from disk into a ringbuffer, to be processed by the RT | |||
@@ -31,9 +34,10 @@ static float seconds_to_buffer = 5.0f; | |||
/* FIXME: can this be made to actually handle capture? */ | |||
/* FIXME: needs error handling everywhere! */ | |||
Disk_Stream::Disk_Stream ( const Track_Header *th, float frame_rate, nframes_t nframes, int channels ) : _th( th ) | |||
Disk_Stream::Disk_Stream ( Track_Header *th, float frame_rate, nframes_t nframes, int channels ) : _th( th ) | |||
{ | |||
_frame = 0; | |||
_thread = 0; | |||
const int blocks = frame_rate * seconds_to_buffer / nframes; | |||
@@ -41,7 +45,7 @@ Disk_Stream::Disk_Stream ( const Track_Header *th, float frame_rate, nframes_t n | |||
size_t bufsize = blocks * nframes * sizeof( sample_t ); | |||
for ( int i = channels(); i-- ) | |||
for ( int i = channels; i--; ) | |||
_rb[ i ] = jack_ringbuffer_create( bufsize ); | |||
sem_init( &_blocks, 0, blocks ); | |||
@@ -49,18 +53,18 @@ Disk_Stream::Disk_Stream ( const Track_Header *th, float frame_rate, nframes_t n | |||
run(); | |||
} | |||
virtual ~Disk_Stream::Disk_Stream ( ) | |||
Disk_Stream::~Disk_Stream ( ) | |||
{ | |||
_th = NULL; | |||
sem_destroy( &_blocks ); | |||
for ( int i = channels(); i-- ) | |||
for ( int i = channels(); i--; ) | |||
jack_ringbuffer_free( _rb[ i ] ); | |||
} | |||
Audio_Track * | |||
Disk_Stream::track ( void ) const | |||
Disk_Stream::track ( void ) | |||
{ | |||
return (Audio_Track*)_th->track(); | |||
} | |||
@@ -69,15 +73,23 @@ Disk_Stream::track ( void ) const | |||
void | |||
Disk_Stream::run ( void ) | |||
{ | |||
if ( pthread_create( 0, 0, &Disk_Stream::io_thread, this ) != 0 ) | |||
if ( pthread_create( &_thread, NULL, &Disk_Stream::io_thread, this ) != 0 ) | |||
/* error */; | |||
} | |||
/* void */ | |||
/* DIsk_Stream::shutdown ( void ) */ | |||
/* { */ | |||
/* pthread_join( &_thread, NULL ); */ | |||
/* } */ | |||
/* static wrapper */ | |||
void | |||
void * | |||
Disk_Stream::io_thread ( void *arg ) | |||
{ | |||
((Disk_Stream*)arg)->io_thread(); | |||
return NULL; | |||
} | |||
/* THREAD: IO */ | |||
@@ -111,13 +123,13 @@ Disk_Stream::io_thread ( void ) | |||
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */ | |||
for ( int i = channels(); i-- ) | |||
for ( int i = channels(); i--; ) | |||
{ | |||
int k = 0; | |||
for ( int j = i; j < _nframes; j += channels() ) | |||
for ( unsigned int j = i; j < _nframes; j += channels() ) | |||
cbuf[ k++ ] = buf[ j ]; | |||
jack_ringbuffer_write( _rb[ i ], cbuf, block_size ); | |||
jack_ringbuffer_write( _rb[ i ], (char*)cbuf, block_size ); | |||
} | |||
} | |||
@@ -129,15 +141,16 @@ Disk_Stream::io_thread ( void ) | |||
/** take a block from the ringbuffers and send it out the track's | |||
* ports */ | |||
void | |||
Disk_Stream::process ( vector <Port*> ports ) | |||
Disk_Stream::process ( void ) | |||
{ | |||
const size_t block_size = _nframes * sizeof( sample_t ); | |||
for ( int i = channels(); i-- ) | |||
for ( int i = channels(); i--; ) | |||
{ | |||
sample_t *buf = _th->output[ i ]->buffer(); | |||
sample_t *buf = (_th->output)[ i ].buffer( _nframes ); | |||
jack_ringbuffer_read( _rb[ i ], buf, block_size ); | |||
/* FIXME: handle underrun */ | |||
jack_ringbuffer_read( _rb[ i ], (char*)buf, block_size ); | |||
} | |||
block_processed(); | |||
@@ -19,20 +19,29 @@ | |||
#pragma once | |||
#include "Port.H" // for nframes_t | |||
#include <jack/ringbuffer.h> | |||
#include <semaphore.h> | |||
#include <errno.h> | |||
#include <pthread.h> | |||
#include <vector> | |||
using std::vector; | |||
class Track_Header; | |||
class Audio_Track; | |||
class Disk_Stream | |||
{ | |||
const Track_Header *_th; /* Track_Header we whould be playing */ | |||
pthread_t _thread; | |||
Track_Header *_th; /* Track_Header we whould be playing */ | |||
nframes_t _nframes; | |||
nframes_t _frame; | |||
vector <jack_ringbuffer_t *> _rb; | |||
@@ -42,19 +51,21 @@ class Disk_Stream | |||
int channels ( void ) const { return _rb.size(); } | |||
Audio_Track * track ( void ) const; | |||
Audio_Track * track ( void ); | |||
static void *io_thread ( void *arg ); | |||
protected: | |||
void block_processed ( void ) { sem_post( &_blocks ); } | |||
bool wait_for_block ( void ) { while ( sem_wait( &_work ) == EINTR ); return true; } | |||
bool wait_for_block ( void ) { while ( sem_wait( &_blocks ) == EINTR ); return true; } | |||
public: | |||
/* must be set before any Disk_Streams are created */ | |||
static float seconds_to_buffer; | |||
Disk_Stream ( const Track_Header *th, float frame_rate, nframes_t nframes, int channels ); | |||
Disk_Stream ( Track_Header *th, float frame_rate, nframes_t nframes, int channels ); | |||
virtual ~Disk_Stream ( ); | |||
@@ -73,9 +84,8 @@ public: | |||
} | |||
void run ( void ); | |||
void io_thread ( void *arg ); | |||
void read_block ( sample_t *buf ); | |||
void io_thread ( void ); | |||
void process ( vector <Port*> ports ); | |||
void process ( void ); | |||
}; |
@@ -11,6 +11,8 @@ SRCS= \ | |||
Peaks.C \ | |||
Audio_File.C \ | |||
Audio_File_SF.C \ | |||
Port.C \ | |||
Disk_Stream.C \ | |||
Loggable.C \ | |||
OBJS=$(SRCS:.C=.o) | |||
@@ -25,8 +27,10 @@ $(OBJS): Makefile | |||
include ../make.inc | |||
#LIBS:=$(LIBS) -ljack -lpthread | |||
timeline: $(OBJS) | |||
$(CXX) $(CXXFLAGS) $(INCLUDES) $(LIBS) $(OBJS) -o $@ | |||
$(CXX) $(CXXFLAGS) $(INCLUDES) $(LIBS) -ljack -lpthread $(OBJS) -o $@ | |||
clean: | |||
rm -f $(OBJS) timeline makedepend | |||
@@ -17,6 +17,10 @@ | |||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |||
/*******************************************************************************/ | |||
#include "Port.H" | |||
#include <string.h> | |||
/* nframes is the number of frames to buffer */ | |||
Port::Port ( jack_port_t *port ) | |||
{ | |||
@@ -32,11 +36,11 @@ Port::~Port ( ) | |||
void | |||
Port::write ( sample_t *buf, nframes_t nframes ) | |||
{ | |||
memcpy( buffer(), buf, nframes * sizeof( sample_t ) ); | |||
memcpy( buffer( nframes ), buf, nframes * sizeof( sample_t ) ); | |||
} | |||
sample_t * | |||
Port::buffer ( void ) | |||
Port::buffer ( nframes_t nframes ) | |||
{ | |||
return (sample_t*)jack_port_get_buffer( _port ); | |||
return (sample_t*)jack_port_get_buffer( _port, nframes ); | |||
} |
@@ -17,6 +17,14 @@ | |||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |||
/*******************************************************************************/ | |||
#pragma once | |||
#include <jack/jack.h> | |||
typedef float sample_t; | |||
//typedef jack_nframes_t nframes_t; | |||
typedef unsigned long nframes_t; | |||
class Port | |||
{ | |||
jack_port_t *_port; | |||
@@ -31,5 +39,5 @@ public: | |||
const char * name ( void ) const { return _name; } | |||
void write ( sample_t *buf, nframes_t nframes ); | |||
sample_t *buffer ( nframes_t nframes ); | |||
}; |
@@ -551,7 +551,7 @@ Region::normalize ( void ) | |||
again? */ | |||
/* FIXME: should fade-out/fade-ins not be handled here? */ | |||
nframes_t | |||
Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ) | |||
Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ) const | |||
{ | |||
const Range &r = _range; | |||
@@ -206,7 +206,7 @@ public: | |||
void normalize ( void ); | |||
nframes_t read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ); | |||
nframes_t read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ) const; | |||
}; | |||
#endif |
@@ -30,6 +30,10 @@ | |||
#include "Loggable.H" | |||
#include "Port.H" | |||
#include <vector> | |||
using std::vector; | |||
class Track_Header : public Fl_Group, public Loggable | |||
{ | |||
@@ -68,6 +72,8 @@ public: | |||
Fl_Pack *takes; | |||
vector <Port> output; /* output ports... */ | |||
const char *class_name ( void ) { return "Track_Header"; } | |||