From fbb4b17dcadb83c609ac7682180ce3a613732063 Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Wed, 16 Apr 2008 03:44:31 -0500 Subject: [PATCH] Split Disk_Stream into a base class and Playback_DS and Record_DS. --- Timeline/Audio_File.H | 6 +- Timeline/Audio_File_SF.C | 49 +++++++- Timeline/Audio_File_SF.H | 2 + Timeline/Disk_Stream.C | 209 +---------------------------------- Timeline/Disk_Stream.H | 36 +++--- Timeline/Makefile | 9 +- Timeline/Playback_DS.C | 227 ++++++++++++++++++++++++++++++++++++++ Timeline/Playback_DS.H | 40 +++++++ Timeline/Port.C | 13 ++- Timeline/Port.H | 9 +- Timeline/Record_DS.C | 155 ++++++++++++++++++++++++++ Timeline/Record_DS.H | 55 +++++++++ Timeline/Region.C | 3 +- Timeline/Timeline.C | 8 +- Timeline/Track_Header.C | 34 ++++-- Timeline/Track_Header.H | 14 ++- Timeline/{dsp.c => dsp.C} | 0 17 files changed, 616 insertions(+), 253 deletions(-) create mode 100644 Timeline/Playback_DS.C create mode 100644 Timeline/Playback_DS.H create mode 100644 Timeline/Record_DS.C create mode 100644 Timeline/Record_DS.H rename Timeline/{dsp.c => dsp.C} (100%) diff --git a/Timeline/Audio_File.H b/Timeline/Audio_File.H index f8a6a12..63d9f5d 100644 --- a/Timeline/Audio_File.H +++ b/Timeline/Audio_File.H @@ -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 ); diff --git a/Timeline/Audio_File_SF.C b/Timeline/Audio_File_SF.C index 6c8bdd8..6a6c276 100644 --- a/Timeline/Audio_File_SF.C +++ b/Timeline/Audio_File_SF.C @@ -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 ); +} diff --git a/Timeline/Audio_File_SF.H b/Timeline/Audio_File_SF.H index 1384ca8..78cfc59 100644 --- a/Timeline/Audio_File_SF.H +++ b/Timeline/Audio_File_SF.H @@ -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 ); }; diff --git a/Timeline/Disk_Stream.C b/Timeline/Disk_Stream.C index 65da27e..441c1b4 100644 --- a/Timeline/Disk_Stream.C +++ b/Timeline/Disk_Stream.C @@ -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; -} diff --git a/Timeline/Disk_Stream.H b/Timeline/Disk_Stream.H index f702886..11b6682 100644 --- a/Timeline/Disk_Stream.H +++ b/Timeline/Disk_Stream.H @@ -19,7 +19,7 @@ #pragma once -#include "Port.H" // for nframes_t +#include "types.h" #include #include @@ -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 _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 ); + }; diff --git a/Timeline/Makefile b/Timeline/Makefile index ef96d42..e74be8f 100644 --- a/Timeline/Makefile +++ b/Timeline/Makefile @@ -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: diff --git a/Timeline/Playback_DS.C b/Timeline/Playback_DS.C new file mode 100644 index 0000000..737fc8e --- /dev/null +++ b/Timeline/Playback_DS.C @@ -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; +} diff --git a/Timeline/Playback_DS.H b/Timeline/Playback_DS.H new file mode 100644 index 0000000..c5065bf --- /dev/null +++ b/Timeline/Playback_DS.H @@ -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 ); + +}; diff --git a/Timeline/Port.C b/Timeline/Port.C index 90c2886..27c6fb2 100644 --- a/Timeline/Port.C +++ b/Timeline/Port.C @@ -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 ) { diff --git a/Timeline/Port.H b/Timeline/Port.H index 7853494..2ec6acb 100644 --- a/Timeline/Port.H +++ b/Timeline/Port.H @@ -21,24 +21,27 @@ #include -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 ); + }; diff --git a/Timeline/Record_DS.C b/Timeline/Record_DS.C new file mode 100644 index 0000000..9658966 --- /dev/null +++ b/Timeline/Record_DS.C @@ -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; +} diff --git a/Timeline/Record_DS.H b/Timeline/Record_DS.H new file mode 100644 index 0000000..5109310 --- /dev/null +++ b/Timeline/Record_DS.H @@ -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 ); + +}; diff --git a/Timeline/Region.C b/Timeline/Region.C index 1ca6fa9..a168483 100644 --- a/Timeline/Region.C +++ b/Timeline/Region.C @@ -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; diff --git a/Timeline/Timeline.C b/Timeline/Timeline.C index 33518d7..2415e4b 100644 --- a/Timeline/Timeline.C +++ b/Timeline/Timeline.C @@ -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; } } diff --git a/Timeline/Track_Header.C b/Timeline/Track_Header.C index c7d5c6e..ad59847 100644 --- a/Timeline/Track_Header.C +++ b/Timeline/Track_Header.C @@ -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 ); } diff --git a/Timeline/Track_Header.H b/Timeline/Track_Header.H index b58d512..a72957a 100644 --- a/Timeline/Track_Header.H +++ b/Timeline/Track_Header.H @@ -30,11 +30,16 @@ #include "Loggable.H" -#include "Port.H" +// #include "Port.H" + + #include 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 input; vector output; /* output ports... */ - Disk_Stream *diskstream; + + Playback_DS *playback_ds; + Record_DS *record_ds; const char *class_name ( void ) { return "Track_Header"; } diff --git a/Timeline/dsp.c b/Timeline/dsp.C similarity index 100% rename from Timeline/dsp.c rename to Timeline/dsp.C