Actually get playback through JACK, kind of.
This commit is contained in:
parent
0dff4be584
commit
7f43a99030
|
@ -25,6 +25,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
Audio_File_SF *
|
||||
Audio_File_SF::from_file ( const char *filename )
|
||||
{
|
||||
|
@ -100,6 +102,11 @@ Audio_File_SF::read ( sample_t *buf, int channel, nframes_t len )
|
|||
return sf_readf_float( _in, buf, len );
|
||||
else
|
||||
{
|
||||
if ( len > 256 * 100 )
|
||||
printf( "warning: attempt to read an insane number of frames (%lu) from soundfile\n", len );
|
||||
|
||||
printf( "len = %lu, channels = %d\n", len, _channels );
|
||||
|
||||
sample_t *tmp = new sample_t[ len * _channels ];
|
||||
|
||||
nframes_t rlen = sf_readf_float( _in, tmp, len );
|
||||
|
@ -118,6 +125,8 @@ Audio_File_SF::read ( sample_t *buf, int channel, nframes_t len )
|
|||
nframes_t
|
||||
Audio_File_SF::read ( sample_t *buf, int channel, nframes_t start, nframes_t end )
|
||||
{
|
||||
assert( end > start );
|
||||
|
||||
open();
|
||||
|
||||
seek( start );
|
||||
|
|
|
@ -99,7 +99,8 @@ Audio_Track::handle ( int m )
|
|||
return 0;
|
||||
}
|
||||
|
||||
Region *r = new Region( c, this, timeline->xoffset + timeline->x_to_ts( Fl::event_x() - x() ) );
|
||||
// Region *r =
|
||||
new Region( c, this, timeline->xoffset + timeline->x_to_ts( Fl::event_x() - x() ) );
|
||||
|
||||
redraw();
|
||||
return 1;
|
||||
|
@ -123,7 +124,7 @@ Audio_Track::play ( sample_t *buf, nframes_t frame, nframes_t nframes, int chann
|
|||
sample_t *cbuf = new sample_t[ nframes ];
|
||||
|
||||
/* quick and dirty--let the regions figure out coverage for themselves */
|
||||
for ( list <Track_Widget *>::const_iterator i = _widgets.begin(); i != _widgets.end(); i++ )
|
||||
for ( list <Track_Widget *>::const_iterator i = _widgets.begin(); i != _widgets.end(); ++i )
|
||||
{
|
||||
const Region *r = (Region*)(*i);
|
||||
|
||||
|
@ -141,6 +142,15 @@ Audio_Track::play ( sample_t *buf, nframes_t frame, nframes_t nframes, int chann
|
|||
}
|
||||
}
|
||||
|
||||
delete[] cbuf;
|
||||
|
||||
/* FIXME: bogus */
|
||||
return nframes;
|
||||
}
|
||||
|
||||
/* /\* THREAD: RT *\/ */
|
||||
/* nframes_t */
|
||||
/* Audio_Track::process ( nframes_t nframes ) */
|
||||
/* { */
|
||||
/* return disktream->process( nframes ); */
|
||||
/* } */
|
||||
|
|
|
@ -46,7 +46,7 @@ Disk_Stream::Disk_Stream ( Track_Header *th, float frame_rate, nframes_t nframes
|
|||
size_t bufsize = blocks * nframes * sizeof( sample_t );
|
||||
|
||||
for ( int i = channels; i--; )
|
||||
_rb[ i ] = jack_ringbuffer_create( bufsize );
|
||||
_rb.push_back( jack_ringbuffer_create( bufsize ) );
|
||||
|
||||
sem_init( &_blocks, 0, blocks );
|
||||
|
||||
|
@ -97,10 +97,27 @@ Disk_Stream::io_thread ( void *arg )
|
|||
void
|
||||
Disk_Stream::read_block ( sample_t *buf )
|
||||
{
|
||||
|
||||
/* 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 */
|
||||
|
@ -119,6 +136,8 @@ Disk_Stream::io_thread ( void )
|
|||
|
||||
while ( wait_for_block() )
|
||||
{
|
||||
// printf( "IO: RT thread is ready for more data...\n" );
|
||||
|
||||
read_block( buf );
|
||||
|
||||
/* deinterleave the buffer and stuff it into the per-channel ringbuffers */
|
||||
|
@ -140,8 +159,8 @@ Disk_Stream::io_thread ( void )
|
|||
/* THREAD: RT */
|
||||
/** take a block from the ringbuffers and send it out the track's
|
||||
* ports */
|
||||
void
|
||||
Disk_Stream::process ( void )
|
||||
nframes_t
|
||||
Disk_Stream::process ( nframes_t nframes )
|
||||
{
|
||||
const size_t block_size = _nframes * sizeof( sample_t );
|
||||
|
||||
|
@ -154,4 +173,7 @@ Disk_Stream::process ( void )
|
|||
}
|
||||
|
||||
block_processed();
|
||||
|
||||
/* FIXME: bogus */
|
||||
return nframes;
|
||||
}
|
||||
|
|
|
@ -86,6 +86,6 @@ public:
|
|||
void run ( void );
|
||||
void read_block ( sample_t *buf );
|
||||
void io_thread ( void );
|
||||
void process ( void );
|
||||
nframes_t process ( nframes_t nframes );
|
||||
|
||||
};
|
||||
|
|
|
@ -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. */
|
||||
/*******************************************************************************/
|
||||
|
||||
#include "Engine.H"
|
||||
|
||||
#include "Timeline.H" // for process()
|
||||
|
||||
#define APP_NAME "Non-DAW" // FIXME: wrong place for this!
|
||||
|
||||
/* This is the home of the JACK process callback (does this *really*
|
||||
need to be a class?) */
|
||||
|
||||
Engine::Engine ( )
|
||||
{
|
||||
_client = NULL;
|
||||
_buffers_dropped = 0;
|
||||
}
|
||||
|
||||
|
||||
/* static wrapper */
|
||||
int
|
||||
Engine::process ( nframes_t nframes, void *arg )
|
||||
{
|
||||
return ((Engine*)arg)->process( nframes );
|
||||
}
|
||||
|
||||
/* THREAD: RT */
|
||||
int
|
||||
Engine::process ( nframes_t nframes )
|
||||
{
|
||||
jack_position_t pos;
|
||||
jack_transport_state_t ts;
|
||||
|
||||
ts = jack_transport_query( _client, &pos );
|
||||
|
||||
if ( ts != JackTransportRolling )
|
||||
return 0;
|
||||
|
||||
if ( ! trylock() )
|
||||
{
|
||||
/* the data structures we need to access here (tracks and
|
||||
* their ports, but not track contents) may be in an
|
||||
* inconsistent state at the moment. Just punt and drop this
|
||||
* buffer. */
|
||||
++_buffers_dropped;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* handle chicken/egg problem */
|
||||
if ( timeline )
|
||||
/* this will initiate the process() call graph for the various
|
||||
* number and types of tracks, which will in turn send data out
|
||||
* the appropriate ports. */
|
||||
timeline->process( nframes );
|
||||
|
||||
unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Engine::init ( void )
|
||||
{
|
||||
if (( _client = jack_client_open ( APP_NAME, (jack_options_t)0, NULL )) == 0 )
|
||||
return 0;
|
||||
|
||||
jack_set_process_callback( _client, &Engine::process, this );
|
||||
|
||||
jack_activate( _client );
|
||||
|
||||
/* we don't need to create any ports until tracks are created */
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* 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 "Mutex.H"
|
||||
|
||||
#include <jack/jack.h>
|
||||
|
||||
typedef jack_nframes_t nframes_t;
|
||||
|
||||
class Port;
|
||||
|
||||
class Engine : public Mutex
|
||||
{
|
||||
jack_client_t *_client;
|
||||
|
||||
/* I know locking out the process callback is cheating, even
|
||||
though we use trylock... The thing is, every other DAW does
|
||||
this too and you can hear it in the glitches Ardour and friends
|
||||
produce when reconfiguring I/O... Working out a message queue
|
||||
system would obviously be better, but a DAW isn't a performance
|
||||
instrument anyway, so I think these drop-outs are a reasonable
|
||||
compromise. Obviously, this lock should never be held during
|
||||
blocking operations. */
|
||||
|
||||
int _buffers_dropped; /* buffers dropped because of locking */
|
||||
|
||||
static int process ( nframes_t nframes, void *arg );
|
||||
int process ( nframes_t nframes );
|
||||
|
||||
private:
|
||||
|
||||
friend class Port;
|
||||
jack_client_t * client ( void ) { return _client; }
|
||||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
Engine ( );
|
||||
|
||||
int init ( void );
|
||||
|
||||
nframes_t nframes ( void ) const { return jack_get_buffer_size( _client ); }
|
||||
float frame_rate ( void ) const { return jack_get_sample_rate( _client ); }
|
||||
|
||||
};
|
||||
|
||||
|
||||
extern Engine * engine;
|
|
@ -31,6 +31,12 @@ public:
|
|||
Mutex ( )
|
||||
{
|
||||
// _lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||
pthread_mutex_init( &_lock, NULL );
|
||||
}
|
||||
|
||||
virtual ~Mutex ( )
|
||||
{
|
||||
pthread_mutex_destroy( &_lock );
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "Engine.H"
|
||||
|
||||
/* nframes is the number of frames to buffer */
|
||||
Port::Port ( jack_port_t *port )
|
||||
{
|
||||
|
@ -28,9 +30,17 @@ Port::Port ( jack_port_t *port )
|
|||
_name = jack_port_name( _port );
|
||||
}
|
||||
|
||||
Port::Port ( const char *name )
|
||||
{
|
||||
_name = name;
|
||||
|
||||
_port = jack_port_register( engine->client(), _name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
|
||||
}
|
||||
|
||||
Port::~Port ( )
|
||||
{
|
||||
/* close port? */
|
||||
// jack_port_unregister( engine->client(), _port );
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -33,6 +33,7 @@ class Port
|
|||
public:
|
||||
|
||||
Port ( jack_port_t *port );
|
||||
Port ( const char *name );
|
||||
~Port ( );
|
||||
|
||||
bool connected ( void ) const { return jack_port_connected( _port ); }
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
/*******************************************************************************/
|
||||
/* 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 <pthread.h>
|
||||
|
||||
class RWLock
|
||||
{
|
||||
|
||||
pthread_rwlock_t _lock;
|
||||
|
||||
public:
|
||||
|
||||
RWLock ( )
|
||||
{
|
||||
// _lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||
pthread_rwlock_init( &_lock, NULL );
|
||||
}
|
||||
|
||||
virtual ~RWLock ( )
|
||||
{
|
||||
pthread_rwlock_destroy( &_lock );
|
||||
}
|
||||
|
||||
void
|
||||
rdlock ( void )
|
||||
{
|
||||
pthread_rwlock_rdlock( &_lock );
|
||||
}
|
||||
|
||||
void
|
||||
wrlock ( void )
|
||||
{
|
||||
pthread_rwlock_wrlock( &_lock );
|
||||
}
|
||||
|
||||
void
|
||||
unlock ( void )
|
||||
{
|
||||
pthread_rwlock_unlock( &_lock );
|
||||
}
|
||||
|
||||
int
|
||||
tryrdlock ( void )
|
||||
{
|
||||
return pthread_rwlock_tryrdlock( &_lock );
|
||||
}
|
||||
|
||||
int
|
||||
trywrlock ( void )
|
||||
{
|
||||
return pthread_rwlock_trywrlock( &_lock );
|
||||
}
|
||||
|
||||
};
|
|
@ -555,9 +555,10 @@ Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ) co
|
|||
{
|
||||
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 ) )
|
||||
|
||||
/* do nothing if we aren't covered by this frame range */
|
||||
if ( pos > r.offset + length || pos + nframes < r.offset )
|
||||
return 0;
|
||||
|
||||
/* calculate offsets into file and sample buffer */
|
||||
|
@ -576,11 +577,11 @@ Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ) co
|
|||
sofs = pos - r.offset;
|
||||
}
|
||||
|
||||
if ( sofs > nframes )
|
||||
if ( ofs > nframes )
|
||||
return 0;
|
||||
|
||||
const nframes_t start = ofs + r.start + sofs;
|
||||
const nframes_t len = min( cnt, nframes - sofs );
|
||||
const nframes_t len = min( cnt, nframes - ofs );
|
||||
const nframes_t end = start + len;
|
||||
|
||||
if ( len == 0 )
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include "Control_Track.H"
|
||||
#include <FL/Fl_Scrollbar.H>
|
||||
|
||||
#include <FL/Fl_Image.H>
|
||||
#include <FL/Fl_RGB_Image.H> // needed for alpha blending
|
||||
// #include <FL/Fl_Image.H>
|
||||
// #include <FL/Fl_RGB_Image.H> // needed for alpha blending
|
||||
|
||||
#include "Track_Header.H"
|
||||
|
||||
|
@ -153,7 +153,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 = 8; 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 );
|
||||
|
|
|
@ -79,9 +79,9 @@ struct Rectangle
|
|||
|
||||
class Engine;
|
||||
|
||||
#include "Mutex.H"
|
||||
#include "RWLock.H"
|
||||
|
||||
class Timeline : public Fl_Overlay_Window, public Mutex
|
||||
class Timeline : public Fl_Overlay_Window, public RWLock
|
||||
{
|
||||
static void draw_clip ( void * v, int X, int Y, int W, int H );
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
#include "Track_Header.H"
|
||||
|
||||
#include "Disk_Stream.H"
|
||||
#include "Engine.H"
|
||||
|
||||
void
|
||||
Track_Header::cb_input_field ( Fl_Widget *w, void *v )
|
||||
{
|
||||
|
@ -85,6 +88,7 @@ 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 )
|
||||
|
@ -96,7 +100,9 @@ Track_Header::Track_Header ( int X, int Y, int W, int H, const char *L ) :
|
|||
_show_all_takes = false;
|
||||
_size = 1;
|
||||
|
||||
// diskstream = new Disk_Stream( this );
|
||||
output.push_back( Port( "foo" ) );
|
||||
|
||||
diskstream = new Disk_Stream( this, engine->frame_rate(), engine->nframes(), 1 );
|
||||
|
||||
Fl_Group::size( w(), height() );
|
||||
|
||||
|
@ -284,8 +290,12 @@ Track_Header::add_control( Track *t )
|
|||
/* Engine */
|
||||
/**********/
|
||||
|
||||
/* THREAD: RT */
|
||||
nframes_t
|
||||
Track_Header::process ( nframes_t nframes )
|
||||
{
|
||||
return track()->process( nframes );
|
||||
if ( diskstream )
|
||||
return diskstream->process( nframes );
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,9 @@
|
|||
#include "Track_Header.H"
|
||||
// #include "const.h"
|
||||
|
||||
#include "Engine.H"
|
||||
|
||||
Engine *engine;
|
||||
Timeline *timeline;
|
||||
|
||||
void cb_undo ( Fl_Widget *w, void *v )
|
||||
|
@ -78,6 +81,10 @@ main ( int argc, char **argv )
|
|||
Loggable::register_create( "Control_Point", &Control_Point::create );
|
||||
Loggable::register_create( "Track_Header", &Track_Header::create );
|
||||
|
||||
/* we don't really need a pointer for this */
|
||||
engine = new Engine;
|
||||
engine->init();
|
||||
|
||||
timeline = new Timeline( 0, 24, main_window->w(), main_window->h() - 24, "Timeline" );
|
||||
|
||||
Fl_Button *o = new Fl_Button( 0, 0, 50, 24, "undo" );
|
||||
|
|
Loading…
Reference in New Issue