Work on adding playback capability.

pull/3/head
Jonathan Moore Liles 2008-04-07 02:29:30 -05:00
parent 6a6c91250e
commit 30f33a3484
6 changed files with 342 additions and 1 deletions

96
Timeline/Disk_Stream.C Normal file
View File

@ -0,0 +1,96 @@
/*******************************************************************************/
/* 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. */
/*******************************************************************************/
static float 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
thread (or vice-versa). */
/* FIXME: handle termination of IO thread in destructor */
/* FIXME: could all of this not simply be included in the Track_Header
class? */
/** start Disk_Stream thread */
void
Disk_Stream::run ( void )
{
if ( pthread_create( 0, 0, &Disk_Stream::io_thread, this ) != 0 )
/* error */;
}
/* static wrapper */
void
Disk_Stream::io_thread ( void *arg )
{
((Disk_Stream*)arg)->io_thread();
}
/* THREAD: IO */
/** read a block of data from the track into /buf/ */
Disk_Stream::read_block ( sample_t *buf )
{
if ( _th->track()->play( buf, _frame, _nframes, channels() ) )
_frame += nframes;
else
/* error */;
}
/* 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() ];
/* buffer for a single channel */
sample_t *cbuf = new sample_t[ _nframes ];
const size_t block_size = _nframes * sizeof( sample_t );
while ( wait_for_block() )
{
read_block( buf );
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */
for ( int i = channels(); i-- )
{
int k = 0;
for ( int j = i; j < _nframes; j += channels() )
cbuf[ k++ ] = buf[ j ];
jack_ringbuffer_write( _rb[ i ], cbuf, block_size );
}
}
delete[] buf;
delete[] cbuf;
}
/* THREAD: RT */
void
Disk_Stream::process ( nframes_t nframes )
{
_th->channels();
block_processed();
}

91
Timeline/Disk_Stream.H Normal file
View File

@ -0,0 +1,91 @@
/*******************************************************************************/
/* 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 <jack/ringbuffer.h>
#include <semaphore.h>
class Disk_Stream
{
const Track_Header *_th; /* Track_Header we whould be playing */
nframes_t _nframes;
vector <jack_ringbuffer_t *> _rb;
// jack_ringbuffer_t *_rb; /* One interleaved ringbuffer for all channels */
sem_t _blocks; /* semaphore to wake the IO thread with */
int channels ( void ) const { return _rb.size(); }
protected:
void block_processed ( void ) { sem_post( &_blocks ); }
bool wait_for_block ( void ) { while ( sem_wait( &_work ) == EINTR ); return true; }
public:
static float seconds_to_buffer;
Disk_Stream ( const Track_Header *th, float frame_rate, nframes_t nframes, int channels ) : _th( th )
{
_frame = 0;
const int blocks = frame_rate * seconds_to_buffer / nframes;
_nframes = nframes;
size_t bufsize = blocks * nframes * sizeof( sample_t );
for ( int i = channels(); i-- )
_rb[ i ] = jack_ringbuffer_create( bufsize );
sem_init( &_blocks, 0, blocks );
run();
}
virtual ~Disk_Stream ( )
{
_th = NULL;
sem_destroy( &_blocks );
for ( int i = channels(); i-- )
jack_ringbuffer_free( _rb[ i ] );
}
void
resize_buffer ( void )
{
}
void
seek ( nframes_t frame )
{
_frame = frame;
}
void run ( void );
};

52
Timeline/Port.C Normal file
View File

@ -0,0 +1,52 @@
/*******************************************************************************/
/* 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. */
/*******************************************************************************/
/* RT/thread-safe interface to a single jack port. */
/* nframes is the number of frames to buffer */
Port::Port ( jack_port_t *port, nframes_t nframes )
{
_port = port;
_rb = jack_ringbuffer_create( nframes * sizeof( stample_t ) );
_name = jack_port_name( _port );
}
Port::~Port ( )
{
jack_ringbuffer_free( _rb );
}
nframes_t
Port::write ( sample_t *buf, nframes_t nframes )
{
const size_t size = nframes * sizeof( sample_t );
return jack_ringbuffer_write( _rb, buf, size ) / sizeof( sample_t );
}
/* runs in the RT thread! */
void
Port::process ( nframes_t nframes )
{
sample_t *buf = jack_port_get_buffer( _port, nframes );
/* FIXME: check size */
jack_ringbuffer_read( _rb, buf, nframes );
}

37
Timeline/Port.H Normal file
View File

@ -0,0 +1,37 @@
/*******************************************************************************/
/* 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. */
/*******************************************************************************/
class Port
{
jack_ringbuffer_t _rb;
jack_port_t *_port;
const char *_name;
public:
Port ( jack_port_t *port, nframes_t nframes );
~Port ( );
bool connected ( void ) const { return jack_port_connected( _port ); }
const char * name ( void ) const { return _name; }
nframes_t write ( sample_t *buf, nframes_t nframes );
void process ( nframes_t nframes );
};

View File

@ -447,7 +447,7 @@ Region::draw ( int X, int Y, int W, int H )
int rw = timeline->ts_to_x( _r->end - _r->start );
nframes_t end = _r->offset + ( _r->end - _r->start );
// nframes_t end = _r->offset + ( _r->end - _r->start );
/* calculate waveform offset due to scrolling */
nframes_t offset = 0;
@ -537,3 +537,66 @@ Region::normalize ( void )
/* _scale = _clip->peaks( 0 )->normalization_factor( timeline->fpp(), _r->start, _r->end ); */
}
/** read the overlapping part of /channel/ at /pos/ for /nframes/ of
this region into /buf/, where /pos/ is in timeline frames */
/* this runs in the diskstream thread. */
/* FIXME: it is far more efficient to read all the channels from a
multichannel source at once... But how should we handle the case of a
mismatch between the number of channels in this region's source and
the number of channels on the track/buffer this data is being read
for? Would it not be better to simply buffer and deinterlace the
frames in the Audio_File class instead, so that sequential requests
for different channels at the same position avoid hitting the disk
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 )
{
const Range &r = _range;
/* do nothing if we aren't covered by this frame range */
const nframes_t length = r.end - r.start;
if ( ! ( pos > r.offset + length || r.offset + length < pos ) )
return 0;
/* calculate offsets into file and sample buffer */
nframes_t sofs, ofs, cnt;
if ( pos < r.offset )
{
sofs = 0;
ofs = r.offset - pos;
cnt = nframes - ofs;
}
else
{
ofs = 0;
sofs = pos - r.offset;
}
if ( sofs > nframes )
return 0;
const nframes_t start = ofs + r.start + sofs;
const nframes_t len = min( cnt, nframes - sofs );
const nframes_t end = start + len;
if ( len == 0 )
return 0;
/* now that we know how much and where to read, get on with it */
/* FIXME: seeking can be very expensive. Esp. with compressed
* formats. We should attempt to avoid it. But here or in the
* Audio_File class? */
cnt = _clip->read( buf + ofs, channel, start, end );
/* apply gain */
for ( int i = cnt; i--; )
buf[i] *= _scale;
return cnt;
}

View File

@ -206,5 +206,7 @@ public:
void normalize ( void );
nframes_t read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel );
};
#endif