/** begin recording */ void Record_DS::start ( nframes_t frame ) { THREAD_ASSERT( UI ); if ( _recording ) { WARNING( "programming error: attempt to start recording while recording is still in progress" ); return; } /* /\* FIXME: safe to do this here? *\/ */ /* flush(); */ DMESSAGE( "recording started at frame %lu", (unsigned long)frame); _frame = frame; _capture = new Track::Capture; run(); _recording = true; }
/** read /nframes/ from the attached track into /buf/ */ void Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) { THREAD_ASSERT( Playback ); memset( buf, 0, nframes * sizeof( sample_t ) * channels() ); // printf( "IO: attempting to read block @ %lu\n", _frame ); if ( !timeline ) return; while ( timeline->sequence_lock.tryrdlock() ) { if ( _terminate ) return; usleep( 1000 * 10 ); } if ( sequence() ) { if ( ! sequence()->play( buf, _frame + _undelay, nframes, channels() ) ) WARNING( "Programming error?" ); _frame += nframes; } timeline->sequence_lock.unlock(); }
void Meter_Indicator_Module::handle_control_changed ( Port *p ) { THREAD_ASSERT( UI ); /* The engine is already locked by the UI thread at this point in the call-graph, so we can be sure that process() won't be executed concurrently. */ if ( p->connected() ) { p = p->connected_port(); if ( dpm_pack->children() != p->hints.dimensions ) { dpm_pack->clear(); control_value = new float[p->hints.dimensions]; for ( int i = p->hints.dimensions; i--; ) { DPM *dpm = new DPM( x(), y(), w(), h() ); dpm->type( FL_VERTICAL ); align( (Fl_Align)(FL_ALIGN_CENTER | FL_ALIGN_INSIDE ) ); dpm_pack->add( dpm ); dpm_pack->redraw(); control_value[i] = -70.0f; dpm->value( -70.0f ); } redraw(); } } }
/** flush buffers and reset. Must only be called from the RT thread. */ void Disk_Stream::base_flush ( bool is_output ) { THREAD_ASSERT( RT ); /* flush buffers */ for ( int i = _rb.size(); i--; ) jack_ringbuffer_read_advance( _rb[ i ], jack_ringbuffer_read_space( _rb[ i ] ) ); /* sem_destroy( &_blocks ); */ /* if ( is_output ) */ /* sem_init( &_blocks, 0, _total_blocks ); */ /* else */ /* sem_init( &_blocks, 0, 0 ); */ if ( is_output ) { int n; sem_getvalue( &_blocks, &n ); n = _total_blocks - n; while ( n-- ) sem_post( &_blocks ); } else { sem_destroy( &_blocks ); sem_init( &_blocks, 0, 0 ); } }
/** read /nframes/ from the attached track into /buf/ */ void Playback_DS::read_block ( sample_t *buf, nframes_t nframes ) { THREAD_ASSERT( Playback ); memset( buf, 0, nframes * sizeof( sample_t ) * channels() ); /* stupid chicken/egg */ if ( ! timeline ) return; // printf( "IO: attempting to read block @ %lu\n", _frame ); if ( ! sequence() ) { /* FIXME: what to do here? */ // _frame += _nframes; return; } timeline->rdlock(); /* FIXME: how does this work if _delay is not a multiple of bufsize? */ if ( _frame >= _delay ) { if ( ! sequence()->play( buf, _frame - _delay, nframes, channels() ) ) WARNING( "Programming error?" ); } _frame += nframes; timeline->unlock(); }
/** take a single block from the ringbuffers and send it out the * attached track's ports */ nframes_t Playback_DS::process ( nframes_t nframes ) { THREAD_ASSERT( RT ); 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 = track()->output[ i ].buffer( nframes ); if ( jack_ringbuffer_read( _rb[ i ], (char*)buf, block_size ) < block_size ) { ++_xruns; memset( buf, 0, block_size ); /* FIXME: we need to resync somehow */ } /* TODO: figure out a way to stop IO while muted without losing sync */ if ( track()->mute() || ( Track::soloing() && ! track()->solo() ) ) buffer_fill_with_silence( (sample_t*)buf, nframes ); } block_processed(); /* FIXME: bogus */ return nframes; }
/** prepare for capturing */ void Audio_Region::prepare ( void ) { THREAD_ASSERT( Capture ); DMESSAGE( "Preparing capture region" ); log_start(); }
/** read from the attached track's ports and stuff the ringbuffers */ nframes_t Record_DS::process ( nframes_t nframes ) { THREAD_ASSERT( RT ); if ( ! _recording ) return 0; if ( transport->frame < _frame ) return 0; /* DMESSAGE( "recording actually happening at %lu (start frame %lu)", (unsigned long)transport->frame, (unsigned long)_frame); */ nframes_t offset = 0; if ( _frame > transport->frame && _frame < transport->frame + nframes ) { /* The record start frame falls somewhere within the current buffer. We must discard the unneeded portion and only stuff the part requested into the ringbuffer. */ offset = _frame - transport->frame; /* DMESSAGE( "offset = %lu", (unsigned long)offset ); */ } const size_t offset_size = offset * sizeof( sample_t ); const size_t block_size = ( nframes * sizeof( sample_t ) ) - offset_size; for ( int i = channels(); i--; ) { /* read the entire input buffer */ void *buf = track()->input[ i ].buffer( nframes ); /* if ( buffer_is_digital_black( (sample_t*)buf, nframes ) ) */ /* DWARNING( "recording an entirely blank buffer" ); */ /* FIXME: this results in a ringbuffer size that is no longer necessarily a multiple of nframes... how will the other side handle that? */ if ( jack_ringbuffer_write( _rb[ i ], (char*)buf + offset, block_size ) < block_size ) { ++_xruns; memset( buf, 0, block_size ); /* FIXME: we need to resync somehow */ } } block_processed(); /* FIXME: bogus */ return nframes; }
/** write /nframes/ from buf to the capture file of the attached track */ void Record_DS::write_block ( sample_t *buf, nframes_t nframes ) { THREAD_ASSERT( Capture ); /* stupid chicken/egg */ if ( ! ( timeline && sequence() ) ) return; track()->write( _capture, buf, nframes ); _frames_written += nframes; }
/** 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 ) { THREAD_ASSERT( RT ); /* FIXME: non-RT-safe IO */ DMESSAGE( "requesting seek to frame %lu", (unsigned long)frame ); if ( seek_pending() ) printf( "seek error, attempt to seek while seek is pending\n" ); _pending_seek = frame; flush(); }
/** finalize region capture. Assumes that this *is* a captured region and that no other regions refer to the same source */ bool Audio_Region::finalize ( nframes_t frame ) { THREAD_ASSERT( Capture ); DMESSAGE( "finalizing capture region" ); _range.length = frame - _range.start; _clip->close(); _clip->open(); log_end(); return true; }
/** 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 ) { THREAD_ASSERT( RT ); /* FIXME: non-RT-safe IO */ DMESSAGE( "requesting seek to frame %lu", (unsigned long)frame ); if ( seek_pending() ) printf( "seek error, attempt to seek while seek is pending\n" ); _seek_frame = frame; _pending_seek = true; /* wake the IO thread */ block_processed(); }
/** write /nframes/ from buf to the capture file of the attached track */ void Record_DS::write_block ( sample_t *buf, nframes_t nframes ) { THREAD_ASSERT( Capture ); /* stupid chicken/egg */ if ( ! ( timeline && sequence() ) ) return; if ( ! _capture->audio_file ) /* create the file */ track()->record( _capture, _frame ); track()->write( _capture, buf, nframes ); _frames_written += nframes; }
/** finalize the recording process. */ void Record_DS::stop ( nframes_t frame ) { THREAD_ASSERT( UI ); if ( ! _recording ) { WARNING( "programming error: attempt to stop recording when no recording is being made" ); return; } _recording = false; _stop_frame = frame; // detach(); DMESSAGE( "recording finished" ); }
/** write /nframes/ from /buf/ to source. /buf/ is interleaved and must match the channel layout of the write source! */ nframes_t Audio_Region::write ( nframes_t nframes ) { THREAD_ASSERT( Capture ); if ( 0 == ( timeline->ts_to_x( _range.length ) % 20 ) ) { int W = 20; if ( W ) { Fl::lock(); sequence()->damage(FL_DAMAGE_USER1, x(), y(), w(), h()); Fl::unlock(); } } _range.length += nframes; return nframes; }
void Controller_Module::process ( nframes_t nframes ) { THREAD_ASSERT( RT ); if ( type() == SPATIALIZATION ) { return; } if ( control_output[0].connected() ) { float f = control_value; if ( mode() == CV ) { f = *((float*)jack_input[0].buffer( nframes )); const Port *p = control_output[0].connected_port(); if (p->hints.ranged ) { // scale value to range. // we assume that CV values are between 0 and 1 float scale = p->hints.maximum - p->hints.minimum; float offset = p->hints.minimum; f = ( f * scale ) + offset; } } // else // f = *((float*)control_output[0].buffer()); *((float*)control_output[0].buffer()) = f; control_value = f; } }
void thread_queue(thread_t *thread) { THREAD_ASSERT(thread); HASH_ADD_INT(thread_list, pid, thread); }
void thread_set_ready(thread_t *thread) { THREAD_ASSERT(thread); thread->status = THREAD_READY; }
/* 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? */ nframes_t Audio_Region::read ( sample_t *buf, nframes_t pos, nframes_t nframes, int channel ) const { THREAD_ASSERT( Playback ); const Range r = _range; /* do nothing if we aren't covered by this frame range */ if ( pos > r.start + r.length || pos + nframes < r.start ) return 0; /* calculate offsets into file and sample buffer */ nframes_t sofs, /* offset into source */ ofs, /* offset into buffer */ cnt; /* number of frames to read */ cnt = nframes; if ( pos < r.start ) { /* region starts somewhere after the beginning of this buffer */ sofs = 0; ofs = r.start - pos; cnt -= ofs; } else { /* region started before this buffer */ ofs = 0; sofs = pos - r.start; } if ( ofs >= nframes ) return 0; // const nframes_t start = ofs + r.start + sofs; const nframes_t start = r.offset + sofs; const nframes_t len = cnt; if ( len == 0 ) return 0; /* now that we know how much and where to read, get on with it */ // printf( "reading region ofs = %lu, sofs = %lu, %lu-%lu\n", ofs, sofs, start, end ); /* FIXME: keep the declick defults someplace else */ Fade declick; declick.length = 256; declick.type = Fade::Sigmoid; if ( _loop ) { nframes_t lofs = sofs % _loop; nframes_t lstart = r.offset + lofs; if ( lofs + len > _loop ) { /* this buffer covers a loop binary */ /* read the first part */ cnt = _clip->read( buf + ofs, channel, lstart, len - ( ( lofs + len ) - _loop ) ); /* read the second part */ cnt += _clip->read( buf + ofs + cnt, channel, lstart + cnt, len - cnt ); /* TODO: declick/crossfade transition? */ assert( cnt == len ); } else cnt = _clip->read( buf + ofs, channel, lstart, len ); /* this buffer is inside declicking proximity to the loop boundary */ if ( lofs + cnt + declick.length > _loop /* buffer ends within declick length of the end of loop */ && sofs + declick.length < r.length /* not the last loop */ ) { /* */ /* fixme: what if loop is shorter than declick? */ const nframes_t declick_start = _loop - declick.length; /* when the buffer covers the beginning of the * declick, how many frames between the beginning of * the buffer and the beginning of the declick */ const nframes_t declick_onset_offset = declick_start > lofs ? declick_start - lofs : 0; /* how far into the declick we are */ const nframes_t declick_offset = lofs > declick_start ? lofs - declick_start : 0; /* this is the end side of the loop boundary */ const nframes_t fl = cnt - declick_onset_offset; declick.apply( buf + ofs + declick_onset_offset, Fade::Out, declick_offset, fl ); } if ( lofs < declick.length /* buffer begins within declick length of beginning of loop */ && sofs > _loop ) /* not the first loop */ { const nframes_t declick_end = declick.length; const nframes_t click_len = lofs + cnt > declick_end ? declick_end - lofs : cnt; /* this is the beginning of the loop next boundary */ declick.apply( buf + ofs, Fade::In, lofs, click_len ); } } else cnt = _clip->read( buf + ofs, channel, start, len ); if ( ! cnt ) return 0; /* apply gain */ buffer_apply_gain( buf + ofs, cnt, _scale ); /* perform declicking if necessary */ { assert( cnt <= nframes ); Fade fade; fade = declick < _fade_in ? _fade_in : declick; /* do fade in if necessary */ if ( sofs < fade.length ) fade.apply( buf + ofs, Fade::In, sofs, cnt ); fade = declick < _fade_out ? _fade_out : declick; /* do fade out if necessary */ if ( start + fade.length > r.offset + r.length ) fade.apply( buf, Fade::Out, ( start + fade.length ) - ( r.offset + r.length ), cnt ); } return cnt; }