Split Disk_Stream into a base class and Playback_DS and Record_DS.
This commit is contained in:
parent
fc2ed291aa
commit
fbb4b17dca
|
@ -39,7 +39,8 @@ protected:
|
|||
|
||||
|
||||
const char *_filename;
|
||||
nframes_t _length; /* length of file in samples */
|
||||
nframes_t _length; /* length of file in samples */
|
||||
nframes_t _samplerate; /* sample rate */
|
||||
int _channels;
|
||||
|
||||
Peaks *_peaks;
|
||||
|
@ -58,7 +59,7 @@ public:
|
|||
const char *name ( void ) const { return _filename; }
|
||||
nframes_t length ( void ) const { return _length; }
|
||||
int channels ( void ) const { return _channels; }
|
||||
|
||||
nframes_t samplerate ( void ) const { return _samplerate; }
|
||||
// Peaks const * peaks ( void ) { return &_peaks; }
|
||||
|
||||
virtual bool open ( void ) = 0;
|
||||
|
@ -66,6 +67,7 @@ public:
|
|||
virtual void seek ( nframes_t offset ) = 0;
|
||||
virtual nframes_t read ( sample_t *buf, int channel, nframes_t len ) = 0;
|
||||
virtual nframes_t read ( sample_t *buf, int channel, nframes_t start, nframes_t end ) = 0;
|
||||
virtual nframes_t write ( sample_t *buf, nframes_t len ) = 0;
|
||||
|
||||
bool read_peaks( float fpp, nframes_t start, nframes_t end, int *peaks, Peak **pbuf, int *channels );
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ Audio_File_SF::from_file ( const char *filename )
|
|||
SNDFILE *in;
|
||||
SF_INFO si;
|
||||
|
||||
|
||||
Audio_File_SF *c = NULL;
|
||||
|
||||
memset( &si, 0, sizeof( si ) );
|
||||
|
@ -53,9 +52,10 @@ Audio_File_SF::from_file ( const char *filename )
|
|||
c = new Audio_File_SF;
|
||||
|
||||
c->_current_read = 0;
|
||||
c->_filename = strdup( filename );
|
||||
c->_length = si.frames;
|
||||
c->_channels = si.channels;
|
||||
c->_filename = strdup( filename );
|
||||
c->_length = si.frames;
|
||||
c->_samplerate = si.samplerate;
|
||||
c->_channels = si.channels;
|
||||
|
||||
c->_in = in;
|
||||
// sf_close( in );
|
||||
|
@ -68,6 +68,38 @@ invalid:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
Audio_File_SF *
|
||||
Audio_File_SF::create ( const char *filename, nframes_t samplerate, int channels, const char *format )
|
||||
{
|
||||
SF_INFO si;
|
||||
SNDFILE *out;
|
||||
|
||||
memset( &si, 0, sizeof( si ) );
|
||||
|
||||
si.samplerate = samplerate;
|
||||
si.channels = channels;
|
||||
|
||||
/* FIXME: bogus */
|
||||
si.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24 | SF_ENDIAN_CPU;
|
||||
|
||||
if ( ! ( out = sf_open( filename, SFM_WRITE, &si ) ) )
|
||||
{
|
||||
printf( "couldn't create soundfile.\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Audio_File_SF *c = new Audio_File_SF;
|
||||
|
||||
c->_filename = strdup( filename );
|
||||
c->_length = 0;
|
||||
c->_samplerate = samplerate;
|
||||
c->_channels = channels;
|
||||
|
||||
c->_in = out;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool
|
||||
Audio_File_SF::open ( void )
|
||||
{
|
||||
|
@ -146,3 +178,12 @@ Audio_File_SF::read ( sample_t *buf, int channel, nframes_t start, nframes_t end
|
|||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/** write /nframes/ from /buf/ to soundfile. Should be interleaved for
|
||||
* the appropriate number of channels */
|
||||
nframes_t
|
||||
Audio_File_SF::write ( sample_t *buf, nframes_t nframes )
|
||||
{
|
||||
return sf_writef_float( _in, buf, nframes );
|
||||
}
|
||||
|
|
|
@ -34,11 +34,13 @@ class Audio_File_SF : public Audio_File
|
|||
public:
|
||||
|
||||
static Audio_File_SF *from_file ( const char *filename );
|
||||
static Audio_File_SF *create ( const char *filename, nframes_t samplerate, int channels, const char *format );
|
||||
|
||||
bool open ( void );
|
||||
void close ( void );
|
||||
void seek ( nframes_t offset );
|
||||
nframes_t read ( sample_t *buf, int channel, nframes_t len );
|
||||
nframes_t read ( sample_t *buf, int channel, nframes_t start, nframes_t end );
|
||||
nframes_t write ( sample_t *buf, nframes_t nframes );
|
||||
|
||||
};
|
||||
|
|
|
@ -68,15 +68,12 @@ Disk_Stream::Disk_Stream ( Track_Header *th, float frame_rate, nframes_t nframes
|
|||
|
||||
size_t bufsize = _total_blocks * nframes * sizeof( sample_t );
|
||||
|
||||
/* const int blocks = 64; */
|
||||
/* const size_t bufsize = (blocks * (nframes * sizeof( sample_t ))) + sizeof( sample_t ); */
|
||||
|
||||
for ( int i = channels; i--; )
|
||||
_rb.push_back( jack_ringbuffer_create( bufsize ) );
|
||||
|
||||
sem_init( &_blocks, 0, _total_blocks );
|
||||
|
||||
run();
|
||||
// run();
|
||||
}
|
||||
|
||||
Disk_Stream::~Disk_Stream ( )
|
||||
|
@ -109,7 +106,7 @@ Disk_Stream::track ( void )
|
|||
void
|
||||
Disk_Stream::run ( void )
|
||||
{
|
||||
if ( pthread_create( &_thread, NULL, &Disk_Stream::io_thread, this ) != 0 )
|
||||
if ( pthread_create( &_thread, NULL, &Disk_Stream::disk_thread, this ) != 0 )
|
||||
/* error */;
|
||||
}
|
||||
|
||||
|
@ -121,47 +118,6 @@ Disk_Stream::resize ( nframes_t nframes )
|
|||
/* FIXME: to something here! */;
|
||||
}
|
||||
|
||||
bool
|
||||
Disk_Stream::seek_pending ( void )
|
||||
{
|
||||
return _pending_seek != (nframes_t)-1;
|
||||
}
|
||||
|
||||
/* THREAD: RT */
|
||||
/** request that the IO thread perform a seek and rebuffer. This is
|
||||
called for each Disk_Stream whenever the RT thread determines that
|
||||
the transport has jumped to a new position. This is called *before*
|
||||
process. */
|
||||
void
|
||||
Disk_Stream::seek ( nframes_t frame )
|
||||
{
|
||||
printf( "requesting seek\n" );
|
||||
|
||||
if ( seek_pending() )
|
||||
printf( "seek error, attempt to seek while seek is pending\n" );
|
||||
|
||||
_pending_seek = frame;
|
||||
|
||||
/* flush buffers */
|
||||
for ( int i = channels(); i--; )
|
||||
jack_ringbuffer_read_advance( _rb[ i ], jack_ringbuffer_read_space( _rb[ i ] ) );
|
||||
|
||||
/* dirty hack... reset the semaphore. Should we just call sem_init
|
||||
* again instead? */
|
||||
|
||||
/* sem_init( &_blocks, 0, _total_blocks ); */
|
||||
|
||||
int n;
|
||||
sem_getvalue( &_blocks, &n );
|
||||
|
||||
n = _total_blocks - n;
|
||||
|
||||
while ( n-- )
|
||||
sem_post( &_blocks );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* void */
|
||||
/* DIsk_Stream::shutdown ( void ) */
|
||||
|
@ -171,45 +127,15 @@ Disk_Stream::seek ( nframes_t frame )
|
|||
|
||||
/* static wrapper */
|
||||
void *
|
||||
Disk_Stream::io_thread ( void *arg )
|
||||
Disk_Stream::disk_thread ( void *arg )
|
||||
{
|
||||
((Disk_Stream*)arg)->io_thread();
|
||||
((Disk_Stream*)arg)->disk_thread();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* THREAD: IO */
|
||||
/** read /nframes/ from the attached track into /buf/ */
|
||||
void
|
||||
Disk_Stream::read_block ( sample_t *buf, nframes_t nframes )
|
||||
{
|
||||
|
||||
memset( buf, 0, nframes * sizeof( sample_t ) * channels() );
|
||||
|
||||
/* stupid chicken/egg */
|
||||
if ( ! timeline )
|
||||
return;
|
||||
|
||||
// printf( "IO: attempting to read block @ %lu\n", _frame );
|
||||
|
||||
if ( ! track() )
|
||||
{
|
||||
// _frame += _nframes;
|
||||
return;
|
||||
}
|
||||
|
||||
timeline->rdlock();
|
||||
|
||||
if ( track()->play( buf, _frame, nframes, channels() ) )
|
||||
_frame += nframes;
|
||||
else
|
||||
/* error */;
|
||||
|
||||
timeline->unlock();
|
||||
}
|
||||
|
||||
int
|
||||
Disk_Stream::output_buffer_percent ( void )
|
||||
Disk_Stream::buffer_percent ( void )
|
||||
{
|
||||
int n;
|
||||
|
||||
|
@ -217,128 +143,3 @@ Disk_Stream::output_buffer_percent ( void )
|
|||
|
||||
return 100 - (n * 100 / _total_blocks);
|
||||
}
|
||||
|
||||
/* THREAD: IO */
|
||||
void
|
||||
Disk_Stream::io_thread ( void )
|
||||
{
|
||||
|
||||
printf( "IO thread running...\n" );
|
||||
|
||||
/* buffer to hold the interleaved data returned by the track reader */
|
||||
sample_t *buf = new sample_t[ _nframes * channels() ];
|
||||
|
||||
const size_t block_size = _nframes * sizeof( sample_t );
|
||||
|
||||
while ( wait_for_block() )
|
||||
{
|
||||
// printf( "IO: RT thread is ready for more data...\n" );
|
||||
|
||||
// printf( "IO: disk buffer is %3d%% full\r", output_buffer_percent() );
|
||||
|
||||
// lock(); // for seeking
|
||||
|
||||
if ( seek_pending() )
|
||||
{
|
||||
printf( "performing seek\n" );
|
||||
_frame = _pending_seek;
|
||||
_pending_seek = -1;
|
||||
/* finish flushing the buffer */
|
||||
|
||||
/* for ( int i = channels(); i-- ) */
|
||||
/* jack_ringbuffer_write_advance( _rb[ i ], jack_ringbuffer_write_space( _rb[ i ] ) ); */
|
||||
|
||||
}
|
||||
|
||||
/* FIXME: should we not read from disk in larger-than-JACK-buffer blocks? */
|
||||
read_block( buf, _nframes );
|
||||
|
||||
// unlock(); // for seeking
|
||||
|
||||
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */
|
||||
|
||||
for ( int i = channels(); i--; )
|
||||
{
|
||||
while ( jack_ringbuffer_write_space( _rb[ i ] ) < block_size )
|
||||
{
|
||||
printf( "IO: disk buffer overrun!\n" );
|
||||
/* FIXME: is this *really* the right thing to do? */
|
||||
usleep( 2000 );
|
||||
}
|
||||
|
||||
/* deinterleave direcectly into the ringbuffer to avoid
|
||||
* unnecessary copying */
|
||||
|
||||
jack_ringbuffer_data_t rbd[2];
|
||||
|
||||
jack_ringbuffer_get_write_vector( _rb[ i ], rbd );
|
||||
|
||||
if ( rbd[ 0 ].len >= _nframes )
|
||||
/* it'll all fit in one go */
|
||||
buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), _nframes );
|
||||
else if ( rbd[ 1 ].len )
|
||||
{
|
||||
/* there's enough space in the ringbuffer, but it's not contiguous */
|
||||
|
||||
/* do the first half */
|
||||
const nframes_t f = rbd[ 1 ].len / sizeof( sample_t );
|
||||
|
||||
buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), f );
|
||||
|
||||
assert( rbd[ 1 ].len >= (_nframes - f) * sizeof( sample_t ) );
|
||||
|
||||
/* do the second half */
|
||||
buffer_deinterleave_one_channel( (sample_t*)rbd[ 1 ].buf, buf + f, i, channels(), _nframes - f );
|
||||
}
|
||||
else
|
||||
printf( "programming error: expected more space in ringbuffer\n" );
|
||||
|
||||
/* buffer_deinterleave_one_channel( (sample_t*)rbd.buf, buf, i, channels(), _nframes ); */
|
||||
/* jack_ringbuffer_write( _rb[ i ], (char*)cbuf, block_size ); */
|
||||
|
||||
jack_ringbuffer_write_advance( _rb[ i ], _nframes * sizeof( sample_t ) );
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
printf( "IO thread terminating.\n" );
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
/* THREAD: RT */
|
||||
/** take a single block from the ringbuffers and send it out the
|
||||
* attached track's ports */
|
||||
nframes_t
|
||||
Disk_Stream::process ( nframes_t nframes )
|
||||
{
|
||||
const size_t block_size = nframes * sizeof( sample_t );
|
||||
|
||||
// printf( "process: %lu %lu %lu\n", _frame, _frame + nframes, nframes );
|
||||
|
||||
for ( int i = channels(); i--; )
|
||||
{
|
||||
|
||||
void *buf = _th->output[ i ].buffer( nframes );
|
||||
|
||||
if ( jack_ringbuffer_read( _rb[ i ], (char*)buf, block_size ) < block_size )
|
||||
{
|
||||
printf( "RT: buffer underrun (disk can't keep up).\n" );
|
||||
memset( buf, 0, block_size );
|
||||
/* FIXME: we need to resync somehow */
|
||||
}
|
||||
|
||||
/* /\* testing. *\/ */
|
||||
/* FILE *fp = fopen( "testing.au", "a" ); */
|
||||
/* fwrite( buf, block_size, 1, fp ); */
|
||||
/* fclose( fp ); */
|
||||
|
||||
}
|
||||
|
||||
block_processed();
|
||||
|
||||
/* FIXME: bogus */
|
||||
return nframes;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Port.H" // for nframes_t
|
||||
#include "types.h"
|
||||
|
||||
#include <jack/ringbuffer.h>
|
||||
#include <semaphore.h>
|
||||
|
@ -38,30 +38,30 @@ class Audio_Track;
|
|||
class Disk_Stream : public Mutex
|
||||
{
|
||||
|
||||
pthread_t _thread;
|
||||
protected:
|
||||
|
||||
Track_Header *_th; /* Track_Header we whould be playing */
|
||||
pthread_t _thread; /* io thread */
|
||||
|
||||
nframes_t _nframes;
|
||||
nframes_t _frame;
|
||||
Track_Header *_th; /* Track_Header we belong to */
|
||||
|
||||
vector <jack_ringbuffer_t *> _rb;
|
||||
nframes_t _nframes; /* buffer size */
|
||||
|
||||
// jack_ringbuffer_t *_rb; /* One interleaved ringbuffer for all channels */
|
||||
nframes_t _frame; /* location of disk read */
|
||||
|
||||
sem_t _blocks; /* semaphore to wake the IO thread with */
|
||||
vector < jack_ringbuffer_t * >_rb; /* one ringbuffer for each channel */
|
||||
|
||||
int _total_blocks;
|
||||
sem_t _blocks; /* semaphore to wake the IO thread with */
|
||||
|
||||
volatile nframes_t _pending_seek; /* absolute transport position to seek to */
|
||||
int _total_blocks; /* total number of blocks that we can buffer */
|
||||
|
||||
volatile nframes_t _pending_seek; /* absolute transport position to seek to */
|
||||
volatile int _terminate;
|
||||
|
||||
int channels ( void ) const { return _rb.size(); }
|
||||
|
||||
Audio_Track * track ( void );
|
||||
|
||||
static void *io_thread ( void *arg );
|
||||
static void *disk_thread ( void *arg );
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -77,8 +77,7 @@ protected:
|
|||
}
|
||||
}
|
||||
|
||||
void read_block ( sample_t *buf, nframes_t nframes );
|
||||
void io_thread ( void );
|
||||
virtual void disk_thread ( void ) = 0;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -90,11 +89,14 @@ public:
|
|||
virtual ~Disk_Stream ( );
|
||||
|
||||
void resize ( nframes_t nframes );
|
||||
void seek ( nframes_t frame );
|
||||
bool seek_pending ( void );
|
||||
|
||||
/* void seek ( nframes_t frame ); */
|
||||
/* bool seek_pending ( void ); */
|
||||
|
||||
void run ( void );
|
||||
nframes_t process ( nframes_t nframes );
|
||||
|
||||
int output_buffer_percent ( void );
|
||||
virtual nframes_t process ( nframes_t nframes ) = 0;
|
||||
|
||||
int buffer_percent ( void );
|
||||
|
||||
};
|
||||
|
|
|
@ -13,12 +13,15 @@ SRCS= \
|
|||
Audio_File_SF.C \
|
||||
Port.C \
|
||||
Disk_Stream.C \
|
||||
dsp.c \
|
||||
Playback_DS.C \
|
||||
Record_DS.C \
|
||||
dsp.C \
|
||||
Engine.C \
|
||||
Transport.C \
|
||||
Loggable.C \
|
||||
|
||||
OBJS=$(SRCS:.C=.o)
|
||||
OBJS:=$(SRCS:.C=.o)
|
||||
# OBJS:=$(OBJS:.c=.o)
|
||||
|
||||
INCLUDES=-I../Engine -I../FL
|
||||
|
||||
|
@ -33,6 +36,8 @@ include ../make.inc
|
|||
#LIBS:=$(LIBS) -ljack -lpthread
|
||||
|
||||
timeline: $(OBJS)
|
||||
echo $(SRCS) >/dev/stderr
|
||||
echo $(OBJS) >/dev/stderr
|
||||
$(CXX) $(CXXFLAGS) $(INCLUDES) $(LIBS) -ljack -lpthread $(OBJS) -o $@
|
||||
|
||||
clean:
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2008 Jonathan Moore Liles */
|
||||
/* */
|
||||
/* 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; see the file COPYING. If not,write to the Free Software */
|
||||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
/*******************************************************************************/
|
||||
|
||||
|
||||
/* Handles streaming regions from disk to track outputs. */
|
||||
|
||||
/* FIXME: we shouldn't depend on these */
|
||||
#include "Timeline.H"
|
||||
#include "Engine.H"
|
||||
#include "Audio_Track.H"
|
||||
#include "Track_Header.H"
|
||||
#include "Port.H"
|
||||
#include "Playback_DS.H"
|
||||
|
||||
|
||||
#include "dsp.h"
|
||||
|
||||
bool
|
||||
Playback_DS::seek_pending ( void )
|
||||
{
|
||||
return _pending_seek != (nframes_t)-1;
|
||||
}
|
||||
|
||||
/* THREAD: RT */
|
||||
/** request that the IO thread perform a seek and rebuffer. This is
|
||||
called for each Disk_Stream whenever the RT thread determines that
|
||||
the transport has jumped to a new position. This is called *before*
|
||||
process. */
|
||||
void
|
||||
Playback_DS::seek ( nframes_t frame )
|
||||
{
|
||||
printf( "requesting seek\n" );
|
||||
|
||||
if ( seek_pending() )
|
||||
printf( "seek error, attempt to seek while seek is pending\n" );
|
||||
|
||||
_pending_seek = frame;
|
||||
|
||||
/* flush buffers */
|
||||
for ( int i = channels(); i--; )
|
||||
jack_ringbuffer_read_advance( _rb[ i ], jack_ringbuffer_read_space( _rb[ i ] ) );
|
||||
|
||||
/* dirty hack... reset the semaphore. Should we just call sem_init
|
||||
* again instead? */
|
||||
|
||||
/* sem_init( &_blocks, 0, _total_blocks ); */
|
||||
|
||||
int n;
|
||||
sem_getvalue( &_blocks, &n );
|
||||
|
||||
n = _total_blocks - n;
|
||||
|
||||
while ( n-- )
|
||||
sem_post( &_blocks );
|
||||
|
||||
}
|
||||
|
||||
/* THREAD: IO */
|
||||
/** read /nframes/ from the attached track into /buf/ */
|
||||
void
|
||||
Playback_DS::read_block ( sample_t *buf, nframes_t nframes )
|
||||
{
|
||||
|
||||
memset( buf, 0, nframes * sizeof( sample_t ) * channels() );
|
||||
|
||||
/* stupid chicken/egg */
|
||||
if ( ! timeline )
|
||||
return;
|
||||
|
||||
// printf( "IO: attempting to read block @ %lu\n", _frame );
|
||||
|
||||
if ( ! track() )
|
||||
{
|
||||
// _frame += _nframes;
|
||||
return;
|
||||
}
|
||||
|
||||
timeline->rdlock();
|
||||
|
||||
if ( track()->play( buf, _frame, nframes, channels() ) )
|
||||
_frame += nframes;
|
||||
else
|
||||
/* error */;
|
||||
|
||||
timeline->unlock();
|
||||
}
|
||||
|
||||
/* THREAD: IO */
|
||||
void
|
||||
Playback_DS::disk_thread ( void )
|
||||
{
|
||||
|
||||
printf( "IO thread running...\n" );
|
||||
|
||||
/* buffer to hold the interleaved data returned by the track reader */
|
||||
sample_t *buf = new sample_t[ _nframes * channels() ];
|
||||
|
||||
const size_t block_size = _nframes * sizeof( sample_t );
|
||||
|
||||
while ( wait_for_block() )
|
||||
{
|
||||
// printf( "IO: RT thread is ready for more data...\n" );
|
||||
|
||||
// printf( "IO: disk buffer is %3d%% full\r", output_buffer_percent() );
|
||||
|
||||
// lock(); // for seeking
|
||||
|
||||
if ( seek_pending() )
|
||||
{
|
||||
printf( "performing seek\n" );
|
||||
_frame = _pending_seek;
|
||||
_pending_seek = -1;
|
||||
/* finish flushing the buffer */
|
||||
|
||||
/* for ( int i = channels(); i-- ) */
|
||||
/* jack_ringbuffer_write_advance( _rb[ i ], jack_ringbuffer_write_space( _rb[ i ] ) ); */
|
||||
|
||||
}
|
||||
|
||||
/* FIXME: should we not read from disk in larger-than-JACK-buffer blocks? */
|
||||
read_block( buf, _nframes );
|
||||
|
||||
// unlock(); // for seeking
|
||||
|
||||
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */
|
||||
|
||||
for ( int i = channels(); i--; )
|
||||
{
|
||||
while ( jack_ringbuffer_write_space( _rb[ i ] ) < block_size )
|
||||
{
|
||||
printf( "IO: disk buffer overrun!\n" );
|
||||
/* FIXME: is this *really* the right thing to do? */
|
||||
usleep( 2000 );
|
||||
}
|
||||
|
||||
/* deinterleave direcectly into the ringbuffer to avoid
|
||||
* unnecessary copying */
|
||||
|
||||
jack_ringbuffer_data_t rbd[2];
|
||||
|
||||
jack_ringbuffer_get_write_vector( _rb[ i ], rbd );
|
||||
|
||||
if ( rbd[ 0 ].len >= _nframes )
|
||||
/* it'll all fit in one go */
|
||||
buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), _nframes );
|
||||
else if ( rbd[ 1 ].len )
|
||||
{
|
||||
/* there's enough space in the ringbuffer, but it's not contiguous */
|
||||
|
||||
/* do the first half */
|
||||
const nframes_t f = rbd[ 1 ].len / sizeof( sample_t );
|
||||
|
||||
buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), f );
|
||||
|
||||
assert( rbd[ 1 ].len >= (_nframes - f) * sizeof( sample_t ) );
|
||||
|
||||
/* do the second half */
|
||||
buffer_deinterleave_one_channel( (sample_t*)rbd[ 1 ].buf, buf + f, i, channels(), _nframes - f );
|
||||
}
|
||||
else
|
||||
printf( "programming error: expected more space in ringbuffer\n" );
|
||||
|
||||
/* buffer_deinterleave_one_channel( (sample_t*)rbd.buf, buf, i, channels(), _nframes ); */
|
||||
/* jack_ringbuffer_write( _rb[ i ], (char*)cbuf, block_size ); */
|
||||
|
||||
jack_ringbuffer_write_advance( _rb[ i ], _nframes * sizeof( sample_t ) );
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
printf( "IO thread terminating.\n" );
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
/* THREAD: RT */
|
||||
/** take a single block from the ringbuffers and send it out the
|
||||
* attached track's ports */
|
||||
nframes_t
|
||||
Playback_DS::process ( nframes_t nframes )
|
||||
{
|
||||
const size_t block_size = nframes * sizeof( sample_t );
|
||||
|
||||
// printf( "process: %lu %lu %lu\n", _frame, _frame + nframes, nframes );
|
||||
|
||||
for ( int i = channels(); i--; )
|
||||
{
|
||||
|
||||
void *buf = _th->output[ i ].buffer( nframes );
|
||||
|
||||
if ( jack_ringbuffer_read( _rb[ i ], (char*)buf, block_size ) < block_size )
|
||||
{
|
||||
printf( "RT: buffer underrun (disk can't keep up).\n" );
|
||||
memset( buf, 0, block_size );
|
||||
/* FIXME: we need to resync somehow */
|
||||
}
|
||||
|
||||
/* /\* testing. *\/ */
|
||||
/* FILE *fp = fopen( "testing.au", "a" ); */
|
||||
/* fwrite( buf, block_size, 1, fp ); */
|
||||
/* fclose( fp ); */
|
||||
|
||||
}
|
||||
|
||||
block_processed();
|
||||
|
||||
/* FIXME: bogus */
|
||||
return nframes;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2008 Jonathan Moore Liles */
|
||||
/* */
|
||||
/* 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; see the file COPYING. If not,write to the Free Software */
|
||||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
/*******************************************************************************/
|
||||
|
||||
#include "Disk_Stream.H"
|
||||
|
||||
class Playback_DS : public Disk_Stream
|
||||
{
|
||||
|
||||
void read_block ( sample_t *buf, nframes_t nframes );
|
||||
void disk_thread ( void );
|
||||
|
||||
public:
|
||||
|
||||
Playback_DS ( Track_Header *th, float frame_rate, nframes_t nframes, int channels ) :
|
||||
Disk_Stream( th, frame_rate, nframes, channels )
|
||||
{
|
||||
run();
|
||||
}
|
||||
|
||||
bool seek_pending ( void );
|
||||
void seek ( nframes_t frame );
|
||||
nframes_t process ( nframes_t nframes );
|
||||
|
||||
};
|
|
@ -30,11 +30,14 @@ Port::Port ( jack_port_t *port )
|
|||
_name = jack_port_name( _port );
|
||||
}
|
||||
|
||||
Port::Port ( const char *name )
|
||||
Port::Port ( const char *name, direction_e dir )
|
||||
{
|
||||
_name = name;
|
||||
|
||||
_port = jack_port_register( engine->client(), _name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
|
||||
_port = jack_port_register( engine->client(), _name,
|
||||
JACK_DEFAULT_AUDIO_TYPE,
|
||||
dir == Output ? JackPortIsOutput : JackPortIsInput,
|
||||
0 );
|
||||
}
|
||||
|
||||
Port::~Port ( )
|
||||
|
@ -49,6 +52,12 @@ Port::write ( sample_t *buf, nframes_t nframes )
|
|||
memcpy( buffer( nframes ), buf, nframes * sizeof( sample_t ) );
|
||||
}
|
||||
|
||||
void
|
||||
Port::read ( sample_t *buf, nframes_t nframes )
|
||||
{
|
||||
memcpy( buf, buffer( nframes ), nframes * sizeof( sample_t ) );
|
||||
}
|
||||
|
||||
void *
|
||||
Port::buffer ( nframes_t nframes )
|
||||
{
|
||||
|
|
|
@ -21,24 +21,27 @@
|
|||
|
||||
#include <jack/jack.h>
|
||||
|
||||
typedef float sample_t;
|
||||
//typedef jack_nframes_t nframes_t;
|
||||
#include "types.h"
|
||||
|
||||
class Port
|
||||
{
|
||||
|
||||
jack_port_t *_port;
|
||||
const char *_name;
|
||||
|
||||
public:
|
||||
|
||||
enum direction_e { Output, Input };
|
||||
|
||||
Port ( jack_port_t *port );
|
||||
Port ( const char *name );
|
||||
Port ( const char *name, direction_e dir );
|
||||
~Port ( );
|
||||
|
||||
bool connected ( void ) const { return jack_port_connected( _port ); }
|
||||
const char * name ( void ) const { return _name; }
|
||||
|
||||
void write ( sample_t *buf, nframes_t nframes );
|
||||
void read ( sample_t *buf, nframes_t nframes );
|
||||
void *buffer ( nframes_t nframes );
|
||||
|
||||
};
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2008 Jonathan Moore Liles */
|
||||
/* */
|
||||
/* 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; see the file COPYING. If not,write to the Free Software */
|
||||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
/*******************************************************************************/
|
||||
|
||||
/* Handles streaming from track inputs to disk */
|
||||
|
||||
/* FIXME: we shouldn't depend on these */
|
||||
#include "Timeline.H"
|
||||
#include "Engine.H"
|
||||
#include "Audio_Track.H"
|
||||
#include "Track_Header.H"
|
||||
#include "Port.H"
|
||||
#include "Record_DS.H"
|
||||
|
||||
#include "dsp.h"
|
||||
|
||||
/* THREAD: IO */
|
||||
/** write /nframes/ from buf to the capture file of the attached track */
|
||||
void
|
||||
Record_DS::write_block ( sample_t *buf, nframes_t nframes )
|
||||
{
|
||||
|
||||
/* stupid chicken/egg */
|
||||
if ( ! ( timeline && track() ) )
|
||||
return;
|
||||
|
||||
// timeline->wrlock();
|
||||
|
||||
_af->write( buf, nframes );
|
||||
|
||||
// track()->record( buf, _frame, nframes, channels() );
|
||||
|
||||
// timeline->unlock();
|
||||
}
|
||||
|
||||
/* THREAD: IO */
|
||||
void
|
||||
Record_DS::disk_thread ( void )
|
||||
{
|
||||
|
||||
printf( "IO thread running...\n" );
|
||||
|
||||
/* buffer to hold the interleaved data returned by the track reader */
|
||||
sample_t *buf = new sample_t[ _nframes * channels() ];
|
||||
sample_t *cbuf = new sample_t[ _nframes ];
|
||||
|
||||
const size_t block_size = _nframes * sizeof( sample_t );
|
||||
|
||||
while ( wait_for_block() )
|
||||
{
|
||||
/* pull data from the per-channel ringbuffers and interlace it */
|
||||
|
||||
for ( int i = channels(); i--; )
|
||||
{
|
||||
while ( jack_ringbuffer_read_space( _rb[ i ] ) < block_size )
|
||||
{
|
||||
printf( "IO: disk buffer underrun!\n" );
|
||||
/* FIXME: is this *really* the right thing to do? */
|
||||
usleep( 2000 );
|
||||
}
|
||||
|
||||
/* FIXME: avoid this copy */
|
||||
|
||||
jack_ringbuffer_read( _rb[ i ], (char*)cbuf, block_size );
|
||||
|
||||
buffer_interleave_one_channel( buf, cbuf, i, channels(), _nframes );
|
||||
|
||||
|
||||
/* /\* deinterleave direcectly into the ringbuffer to avoid */
|
||||
/* * unnecessary copying *\/ */
|
||||
|
||||
/* jack_ringbuffer_data_t rbd[2]; */
|
||||
|
||||
/* jack_ringbuffer_get_write_vector( _rb[ i ], rbd ); */
|
||||
|
||||
/* if ( rbd[ 0 ].len >= _nframes ) */
|
||||
/* /\* it'll all fit in one go *\/ */
|
||||
/* buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), _nframes ); */
|
||||
/* else if ( rbd[ 1 ].len ) */
|
||||
/* { */
|
||||
/* /\* there's enough space in the ringbuffer, but it's not contiguous *\/ */
|
||||
|
||||
/* /\* do the first half *\/ */
|
||||
/* const nframes_t f = rbd[ 1 ].len / sizeof( sample_t ); */
|
||||
|
||||
/* buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), f ); */
|
||||
|
||||
/* assert( rbd[ 1 ].len >= (_nframes - f) * sizeof( sample_t ) ); */
|
||||
|
||||
/* /\* do the second half *\/ */
|
||||
/* buffer_deinterleave_one_channel( (sample_t*)rbd[ 1 ].buf, buf + f, i, channels(), _nframes - f ); */
|
||||
/* } */
|
||||
/* else */
|
||||
/* printf( "programming error: expected more space in ringbuffer\n" ); */
|
||||
|
||||
/* /\* buffer_deinterleave_one_channel( (sample_t*)rbd.buf, buf, i, channels(), _nframes ); *\/ */
|
||||
/* /\* jack_ringbuffer_write( _rb[ i ], (char*)cbuf, block_size ); *\/ */
|
||||
|
||||
/* jack_ringbuffer_write_advance( _rb[ i ], _nframes * sizeof( sample_t ) ); */
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
write_block( buf, _nframes );
|
||||
|
||||
}
|
||||
|
||||
printf( "IO thread terminating.\n" );
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
/* THREAD: RT */
|
||||
/** take a single block from the ringbuffers and send it out the
|
||||
* attached track's ports */
|
||||
nframes_t
|
||||
Record_DS::process ( nframes_t nframes )
|
||||
{
|
||||
const size_t block_size = nframes * sizeof( sample_t );
|
||||
|
||||
// printf( "process: %lu %lu %lu\n", _frame, _frame + nframes, nframes );
|
||||
|
||||
for ( int i = channels(); i--; )
|
||||
{
|
||||
void *buf = _th->input[ i ].buffer( nframes );
|
||||
|
||||
if ( jack_ringbuffer_write( _rb[ i ], (char*)buf, block_size ) < block_size )
|
||||
{
|
||||
printf( "RT: buffer overrun (disk can't keep up).\n" );
|
||||
memset( buf, 0, block_size );
|
||||
/* FIXME: we need to resync somehow */
|
||||
}
|
||||
}
|
||||
|
||||
block_processed();
|
||||
|
||||
/* FIXME: bogus */
|
||||
return nframes;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* Copyright (C) 2008 Jonathan Moore Liles */
|
||||
/* */
|
||||
/* 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; see the file COPYING. If not,write to the Free Software */
|
||||
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
/*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Disk_Stream.H"
|
||||
|
||||
#include "Audio_File_SF.H"
|
||||
class Audio_File;
|
||||
|
||||
class Record_DS : public Disk_Stream
|
||||
{
|
||||
|
||||
Audio_File_SF *_af; /* capture file */
|
||||
|
||||
void write_block ( sample_t *buf, nframes_t nframes );
|
||||
void disk_thread ( void );
|
||||
|
||||
public:
|
||||
|
||||
Record_DS ( Track_Header *th, float frame_rate, nframes_t nframes, int channels ) :
|
||||
Disk_Stream( th, frame_rate, nframes, channels )
|
||||
{
|
||||
/* FIXME: we need our semaphore set to 0, no? */
|
||||
|
||||
_af = Audio_File_SF::create( "testing.wav", 48000, 1, "Wav/24" );
|
||||
|
||||
sem_destroy( &_blocks );
|
||||
sem_init( &_blocks, 0, 0 );
|
||||
|
||||
run();
|
||||
}
|
||||
|
||||
/* bool seek_pending ( void ); */
|
||||
/* void seek ( nframes_t frame ); */
|
||||
|
||||
nframes_t process ( nframes_t nframes );
|
||||
|
||||
};
|
|
@ -709,7 +709,8 @@ Region::Fade::apply ( sample_t *buf, Region::Fade::fade_dir_e dir, long start, n
|
|||
const float inc = increment();
|
||||
float fi = ( i - start ) / (float)length;
|
||||
|
||||
buf += i;
|
||||
// buf += i;
|
||||
buf = &buf[ i ];
|
||||
|
||||
nframes_t n = e - i;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
const float UPDATE_FREQ = 0.02f;
|
||||
|
||||
|
||||
#include "Disk_Stream.H"
|
||||
#include "Playback_DS.H"
|
||||
|
||||
#include "Transport.H"
|
||||
|
||||
|
@ -160,7 +160,7 @@ Timeline::Timeline ( int X, int Y, int W, int H, const char* L ) : Fl_Overlay_Wi
|
|||
o->type( Fl_Pack::VERTICAL );
|
||||
o->spacing( 0 );
|
||||
|
||||
for ( int i = 2; i--; )
|
||||
for ( int i = 1; i--; )
|
||||
{
|
||||
// Track_Header *t = new Track_Header( 0, 0, W, 75 );
|
||||
Track_Header *t = new Track_Header( 0, 0, W, 30 );
|
||||
|
@ -699,7 +699,7 @@ Timeline::seek_pending ( void )
|
|||
{
|
||||
Track_Header *t = (Track_Header*)tracks->child( i );
|
||||
|
||||
if ( t->diskstream )
|
||||
r += t->diskstream->output_buffer_percent() < 50;
|
||||
if ( t->playback_ds )
|
||||
r += t->playback_ds->buffer_percent() < 50;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,13 @@
|
|||
|
||||
#include "Track_Header.H"
|
||||
|
||||
#include "Disk_Stream.H"
|
||||
#include "Playback_DS.H"
|
||||
#include "Record_DS.H"
|
||||
|
||||
#include "Engine.H"
|
||||
|
||||
#include "Port.H"
|
||||
|
||||
void
|
||||
Track_Header::cb_input_field ( Fl_Widget *w, void *v )
|
||||
{
|
||||
|
@ -88,8 +92,6 @@ Track_Header::cb_button ( Fl_Widget *w )
|
|||
}
|
||||
}
|
||||
|
||||
#include "Port.H"
|
||||
|
||||
Track_Header::Track_Header ( int X, int Y, int W, int H, const char *L ) :
|
||||
Fl_Group ( X, Y, W, H, L )
|
||||
{
|
||||
|
@ -102,13 +104,20 @@ Track_Header::Track_Header ( int X, int Y, int W, int H, const char *L ) :
|
|||
|
||||
{
|
||||
char pname[40];
|
||||
static int n = 0;
|
||||
snprintf( pname, sizeof( pname ), "out-%d", n++ );
|
||||
static int no = 0, ni = 0;
|
||||
|
||||
snprintf( pname, sizeof( pname ), "out-%d", no++ );
|
||||
|
||||
output.push_back( Port( strdup( pname ), Port::Output ) );
|
||||
|
||||
snprintf( pname, sizeof( pname ), "in-%d", ni++ );
|
||||
|
||||
input.push_back( Port( strdup( pname ), Port::Input ) );
|
||||
|
||||
output.push_back( Port( strdup( pname ) ) );
|
||||
}
|
||||
|
||||
diskstream = new Disk_Stream( this, engine->frame_rate(), engine->nframes(), 1 );
|
||||
playback_ds = new Playback_DS( this, engine->frame_rate(), engine->nframes(), 1 );
|
||||
record_ds = new Record_DS( this, engine->frame_rate(), engine->nframes(), 1 );
|
||||
|
||||
Fl_Group::size( w(), height() );
|
||||
|
||||
|
@ -300,8 +309,11 @@ Track_Header::add_control( Track *t )
|
|||
nframes_t
|
||||
Track_Header::process ( nframes_t nframes )
|
||||
{
|
||||
if ( diskstream )
|
||||
return diskstream->process( nframes );
|
||||
if ( playback_ds )
|
||||
{
|
||||
record_ds->process( nframes );
|
||||
return playback_ds->process( nframes );
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
@ -310,6 +322,6 @@ Track_Header::process ( nframes_t nframes )
|
|||
void
|
||||
Track_Header::seek ( nframes_t frame )
|
||||
{
|
||||
if ( diskstream )
|
||||
return diskstream->seek( frame );
|
||||
if ( playback_ds )
|
||||
return playback_ds->seek( frame );
|
||||
}
|
||||
|
|
|
@ -30,11 +30,16 @@
|
|||
|
||||
#include "Loggable.H"
|
||||
|
||||
#include "Port.H"
|
||||
// #include "Port.H"
|
||||
|
||||
|
||||
#include <vector>
|
||||
using std::vector;
|
||||
|
||||
class Disk_Stream;
|
||||
class Playback_DS;
|
||||
class Record_DS;
|
||||
|
||||
class Port;
|
||||
|
||||
class Track_Header : public Fl_Group, public Loggable
|
||||
{
|
||||
|
@ -73,8 +78,11 @@ public:
|
|||
Fl_Pack *control;
|
||||
Fl_Pack *takes;
|
||||
|
||||
vector <Port> input;
|
||||
vector <Port> output; /* output ports... */
|
||||
Disk_Stream *diskstream;
|
||||
|
||||
Playback_DS *playback_ds;
|
||||
Record_DS *record_ds;
|
||||
|
||||
const char *class_name ( void ) { return "Track_Header"; }
|
||||
|
||||
|
|
Loading…
Reference in New Issue