Browse Source

Split Disk_Stream into a base class and Playback_DS and Record_DS.

tags/non-daw-v1.1.0
Jonathan Moore Liles 17 years ago
parent
commit
fbb4b17dca
17 changed files with 616 additions and 253 deletions
  1. +4
    -2
      Timeline/Audio_File.H
  2. +45
    -4
      Timeline/Audio_File_SF.C
  3. +2
    -0
      Timeline/Audio_File_SF.H
  4. +5
    -204
      Timeline/Disk_Stream.C
  5. +19
    -17
      Timeline/Disk_Stream.H
  6. +7
    -2
      Timeline/Makefile
  7. +227
    -0
      Timeline/Playback_DS.C
  8. +40
    -0
      Timeline/Playback_DS.H
  9. +11
    -2
      Timeline/Port.C
  10. +6
    -3
      Timeline/Port.H
  11. +155
    -0
      Timeline/Record_DS.C
  12. +55
    -0
      Timeline/Record_DS.H
  13. +2
    -1
      Timeline/Region.C
  14. +4
    -4
      Timeline/Timeline.C
  15. +23
    -11
      Timeline/Track_Header.C
  16. +11
    -3
      Timeline/Track_Header.H
  17. +0
    -0
      Timeline/dsp.C

+ 4
- 2
Timeline/Audio_File.H View File

@@ -39,7 +39,8 @@ protected:




const char *_filename; 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; int _channels;


Peaks *_peaks; Peaks *_peaks;
@@ -58,7 +59,7 @@ public:
const char *name ( void ) const { return _filename; } const char *name ( void ) const { return _filename; }
nframes_t length ( void ) const { return _length; } nframes_t length ( void ) const { return _length; }
int channels ( void ) const { return _channels; } int channels ( void ) const { return _channels; }
nframes_t samplerate ( void ) const { return _samplerate; }
// Peaks const * peaks ( void ) { return &_peaks; } // Peaks const * peaks ( void ) { return &_peaks; }


virtual bool open ( void ) = 0; virtual bool open ( void ) = 0;
@@ -66,6 +67,7 @@ public:
virtual void seek ( nframes_t offset ) = 0; 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 len ) = 0;
virtual nframes_t read ( sample_t *buf, int channel, nframes_t start, nframes_t end ) = 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 ); bool read_peaks( float fpp, nframes_t start, nframes_t end, int *peaks, Peak **pbuf, int *channels );




+ 45
- 4
Timeline/Audio_File_SF.C View File

@@ -33,7 +33,6 @@ Audio_File_SF::from_file ( const char *filename )
SNDFILE *in; SNDFILE *in;
SF_INFO si; SF_INFO si;



Audio_File_SF *c = NULL; Audio_File_SF *c = NULL;


memset( &si, 0, sizeof( si ) ); memset( &si, 0, sizeof( si ) );
@@ -53,9 +52,10 @@ Audio_File_SF::from_file ( const char *filename )
c = new Audio_File_SF; c = new Audio_File_SF;


c->_current_read = 0; 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; c->_in = in;
// sf_close( in ); // sf_close( in );
@@ -68,6 +68,38 @@ invalid:
return NULL; 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 bool
Audio_File_SF::open ( void ) 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; 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 );
}

+ 2
- 0
Timeline/Audio_File_SF.H View File

@@ -34,11 +34,13 @@ class Audio_File_SF : public Audio_File
public: public:


static Audio_File_SF *from_file ( const char *filename ); 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 ); bool open ( void );
void close ( void ); void close ( void );
void seek ( nframes_t offset ); 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 len );
nframes_t read ( sample_t *buf, int channel, nframes_t start, nframes_t end ); nframes_t read ( sample_t *buf, int channel, nframes_t start, nframes_t end );
nframes_t write ( sample_t *buf, nframes_t nframes );


}; };

+ 5
- 204
Timeline/Disk_Stream.C View File

@@ -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 ); 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--; ) for ( int i = channels; i--; )
_rb.push_back( jack_ringbuffer_create( bufsize ) ); _rb.push_back( jack_ringbuffer_create( bufsize ) );


sem_init( &_blocks, 0, _total_blocks ); sem_init( &_blocks, 0, _total_blocks );


run();
// run();
} }


Disk_Stream::~Disk_Stream ( ) Disk_Stream::~Disk_Stream ( )
@@ -109,7 +106,7 @@ Disk_Stream::track ( void )
void void
Disk_Stream::run ( 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 */; /* error */;
} }


@@ -121,47 +118,6 @@ Disk_Stream::resize ( nframes_t nframes )
/* FIXME: to something here! */; /* 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 */ /* void */
/* DIsk_Stream::shutdown ( void ) */ /* DIsk_Stream::shutdown ( void ) */
@@ -171,45 +127,15 @@ Disk_Stream::seek ( nframes_t frame )


/* static wrapper */ /* static wrapper */
void * 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; 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 int
Disk_Stream::output_buffer_percent ( void )
Disk_Stream::buffer_percent ( void )
{ {
int n; int n;


@@ -217,128 +143,3 @@ Disk_Stream::output_buffer_percent ( void )


return 100 - (n * 100 / _total_blocks); 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
- 17
Timeline/Disk_Stream.H View File

@@ -19,7 +19,7 @@


#pragma once #pragma once


#include "Port.H" // for nframes_t
#include "types.h"


#include <jack/ringbuffer.h> #include <jack/ringbuffer.h>
#include <semaphore.h> #include <semaphore.h>
@@ -38,30 +38,30 @@ class Audio_Track;
class Disk_Stream : public Mutex 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; volatile int _terminate;


int channels ( void ) const { return _rb.size(); } int channels ( void ) const { return _rb.size(); }


Audio_Track * track ( void ); Audio_Track * track ( void );


static void *io_thread ( void *arg );
static void *disk_thread ( void *arg );


protected: 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: public:


@@ -90,11 +89,14 @@ public:
virtual ~Disk_Stream ( ); virtual ~Disk_Stream ( );


void resize ( nframes_t nframes ); 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 ); 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 );

}; };

+ 7
- 2
Timeline/Makefile View File

@@ -13,12 +13,15 @@ SRCS= \
Audio_File_SF.C \ Audio_File_SF.C \
Port.C \ Port.C \
Disk_Stream.C \ Disk_Stream.C \
dsp.c \
Playback_DS.C \
Record_DS.C \
dsp.C \
Engine.C \ Engine.C \
Transport.C \ Transport.C \
Loggable.C \ Loggable.C \


OBJS=$(SRCS:.C=.o)
OBJS:=$(SRCS:.C=.o)
# OBJS:=$(OBJS:.c=.o)


INCLUDES=-I../Engine -I../FL INCLUDES=-I../Engine -I../FL


@@ -33,6 +36,8 @@ include ../make.inc
#LIBS:=$(LIBS) -ljack -lpthread #LIBS:=$(LIBS) -ljack -lpthread


timeline: $(OBJS) timeline: $(OBJS)
echo $(SRCS) >/dev/stderr
echo $(OBJS) >/dev/stderr
$(CXX) $(CXXFLAGS) $(INCLUDES) $(LIBS) -ljack -lpthread $(OBJS) -o $@ $(CXX) $(CXXFLAGS) $(INCLUDES) $(LIBS) -ljack -lpthread $(OBJS) -o $@


clean: clean:


+ 227
- 0
Timeline/Playback_DS.C View File

@@ -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;
}

+ 40
- 0
Timeline/Playback_DS.H View File

@@ -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 );

};

+ 11
- 2
Timeline/Port.C View File

@@ -30,11 +30,14 @@ Port::Port ( jack_port_t *port )
_name = jack_port_name( _port ); _name = jack_port_name( _port );
} }


Port::Port ( const char *name )
Port::Port ( const char *name, direction_e dir )
{ {
_name = name; _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 ( ) Port::~Port ( )
@@ -49,6 +52,12 @@ Port::write ( sample_t *buf, nframes_t nframes )
memcpy( buffer( nframes ), buf, nframes * sizeof( sample_t ) ); 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 * void *
Port::buffer ( nframes_t nframes ) Port::buffer ( nframes_t nframes )
{ {


+ 6
- 3
Timeline/Port.H View File

@@ -21,24 +21,27 @@


#include <jack/jack.h> #include <jack/jack.h>


typedef float sample_t;
//typedef jack_nframes_t nframes_t;
#include "types.h" #include "types.h"


class Port class Port
{ {

jack_port_t *_port; jack_port_t *_port;
const char *_name; const char *_name;


public: public:


enum direction_e { Output, Input };

Port ( jack_port_t *port ); Port ( jack_port_t *port );
Port ( const char *name );
Port ( const char *name, direction_e dir );
~Port ( ); ~Port ( );


bool connected ( void ) const { return jack_port_connected( _port ); } bool connected ( void ) const { return jack_port_connected( _port ); }
const char * name ( void ) const { return _name; } const char * name ( void ) const { return _name; }


void write ( sample_t *buf, nframes_t nframes ); void write ( sample_t *buf, nframes_t nframes );
void read ( sample_t *buf, nframes_t nframes );
void *buffer ( nframes_t nframes ); void *buffer ( nframes_t nframes );

}; };

+ 155
- 0
Timeline/Record_DS.C View File

@@ -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;
}

+ 55
- 0
Timeline/Record_DS.H View File

@@ -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 );

};

+ 2
- 1
Timeline/Region.C View File

@@ -709,7 +709,8 @@ Region::Fade::apply ( sample_t *buf, Region::Fade::fade_dir_e dir, long start, n
const float inc = increment(); const float inc = increment();
float fi = ( i - start ) / (float)length; float fi = ( i - start ) / (float)length;


buf += i;
// buf += i;
buf = &buf[ i ];


nframes_t n = e - i; nframes_t n = e - i;




+ 4
- 4
Timeline/Timeline.C View File

@@ -32,7 +32,7 @@
const float UPDATE_FREQ = 0.02f; const float UPDATE_FREQ = 0.02f;




#include "Disk_Stream.H"
#include "Playback_DS.H"


#include "Transport.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->type( Fl_Pack::VERTICAL );
o->spacing( 0 ); 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, 75 );
Track_Header *t = new Track_Header( 0, 0, W, 30 ); 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 ); 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;
} }
} }

+ 23
- 11
Timeline/Track_Header.C View File

@@ -19,9 +19,13 @@


#include "Track_Header.H" #include "Track_Header.H"


#include "Disk_Stream.H"
#include "Playback_DS.H"
#include "Record_DS.H"

#include "Engine.H" #include "Engine.H"


#include "Port.H"

void void
Track_Header::cb_input_field ( Fl_Widget *w, void *v ) 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 ) : Track_Header::Track_Header ( int X, int Y, int W, int H, const char *L ) :
Fl_Group ( X, Y, W, H, 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]; 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() ); Fl_Group::size( w(), height() );


@@ -300,8 +309,11 @@ Track_Header::add_control( Track *t )
nframes_t nframes_t
Track_Header::process ( nframes_t nframes ) 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 else
return 0; return 0;
} }
@@ -310,6 +322,6 @@ Track_Header::process ( nframes_t nframes )
void void
Track_Header::seek ( nframes_t frame ) Track_Header::seek ( nframes_t frame )
{ {
if ( diskstream )
return diskstream->seek( frame );
if ( playback_ds )
return playback_ds->seek( frame );
} }

+ 11
- 3
Timeline/Track_Header.H View File

@@ -30,11 +30,16 @@


#include "Loggable.H" #include "Loggable.H"


#include "Port.H"
// #include "Port.H"


#include <vector> #include <vector>
using std::vector; using std::vector;


class Disk_Stream;
class Playback_DS;
class Record_DS;

class Port;


class Track_Header : public Fl_Group, public Loggable class Track_Header : public Fl_Group, public Loggable
{ {
@@ -73,8 +78,11 @@ public:
Fl_Pack *control; Fl_Pack *control;
Fl_Pack *takes; Fl_Pack *takes;


vector <Port> input;
vector <Port> output; /* output ports... */ vector <Port> output; /* output ports... */
Disk_Stream *diskstream;

Playback_DS *playback_ds;
Record_DS *record_ds;


const char *class_name ( void ) { return "Track_Header"; } const char *class_name ( void ) { return "Track_Header"; }




Timeline/dsp.c → Timeline/dsp.C View File


Loading…
Cancel
Save