Work on adding playback capability.
This commit is contained in:
parent
6a6c91250e
commit
30f33a3484
|
@ -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();
|
||||
}
|
|
@ -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 );
|
||||
|
||||
};
|
|
@ -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 );
|
||||
}
|
|
@ -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 );
|
||||
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -206,5 +206,7 @@ public:
|
|||
|
||||
void normalize ( void );
|
||||
|
||||
nframes_t read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel );
|
||||
|
||||
};
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue