2008-06-02 21:41:35 +00:00
/*
2009-10-14 16:10:01 +00:00
Copyright ( C ) 2006 Paul Davis
2011-04-06 15:00:16 +00:00
Author : David Robillard
2008-06-02 21:41:35 +00:00
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 ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <vector>
# include <sys/time.h>
# include <sys/stat.h>
# include <unistd.h>
# include <errno.h>
2013-01-16 17:09:52 +00:00
# include <regex.h>
2008-06-02 21:41:35 +00:00
2009-02-25 18:26:51 +00:00
# include "pbd/pathscanner.h"
# include "pbd/stl_delete.h"
# include "pbd/strsplit.h"
2008-06-02 21:41:35 +00:00
# include <glibmm/miscutils.h>
2014-02-27 20:37:17 -05:00
# include <glibmm/fileutils.h>
2008-06-02 21:41:35 +00:00
2009-02-25 18:26:51 +00:00
# include "evoral/Control.hpp"
2008-09-30 00:45:26 +00:00
2009-02-25 18:26:51 +00:00
# include "ardour/event_type_map.h"
# include "ardour/midi_model.h"
# include "ardour/midi_ring_buffer.h"
2009-10-19 17:05:22 +00:00
# include "ardour/midi_state_tracker.h"
2009-02-25 18:26:51 +00:00
# include "ardour/session.h"
# include "ardour/smf_source.h"
2010-05-24 22:39:36 +00:00
# include "ardour/debug.h"
2008-06-02 21:41:35 +00:00
# include "i18n.h"
using namespace ARDOUR ;
2009-02-17 02:11:49 +00:00
using namespace Glib ;
2010-05-24 22:39:36 +00:00
using namespace PBD ;
2008-06-02 21:41:35 +00:00
2009-02-15 17:30:42 +00:00
/** Constructor used for new internal-to-session files. File cannot exist. */
2010-09-14 15:45:21 +00:00
SMFSource : : SMFSource ( Session & s , const string & path , Source : : Flag flags )
2009-02-17 02:11:49 +00:00
: Source ( s , DataType : : MIDI , path , flags )
2011-03-01 02:04:34 +00:00
, MidiSource ( s , path , flags )
2010-11-09 18:19:53 +00:00
, FileSource ( s , DataType : : MIDI , path , string ( ) , flags )
2009-02-15 01:53:06 +00:00
, Evoral : : SMF ( )
2009-02-15 17:30:42 +00:00
, _last_ev_time_beats ( 0.0 )
, _last_ev_time_frames ( 0 )
2009-09-16 01:08:51 +00:00
, _smf_last_read_end ( 0 )
2010-05-24 15:42:27 +00:00
, _smf_last_read_time ( 0 )
2008-06-02 21:41:35 +00:00
{
2010-12-20 22:51:17 +00:00
/* note that origin remains empty */
2010-11-09 18:19:53 +00:00
2014-04-14 03:03:35 -04:00
if ( init ( _path , false ) ) {
2008-06-02 21:41:35 +00:00
throw failed_constructor ( ) ;
}
2014-04-14 03:03:35 -04:00
assert ( ! Glib : : file_test ( _path , Glib : : FILE_TEST_EXISTS ) ) ;
existence_check ( ) ;
2011-06-01 16:50:12 +00:00
2013-01-21 02:35:53 +00:00
/* file is not opened until write */
2014-01-20 13:38:28 +01:00
if ( flags & Writable ) {
return ;
}
2014-04-04 15:26:44 -04:00
if ( open ( _path ) ) {
2014-01-20 13:38:28 +01:00
throw failed_constructor ( ) ;
}
2014-04-04 15:26:44 -04:00
2014-01-20 13:38:28 +01:00
_open = true ;
2008-06-02 21:41:35 +00:00
}
2009-02-17 02:11:49 +00:00
/** Constructor used for existing internal-to-session files. */
SMFSource : : SMFSource ( Session & s , const XMLNode & node , bool must_exist )
: Source ( s , node )
, MidiSource ( s , node )
, FileSource ( s , node , must_exist )
2009-02-15 17:30:42 +00:00
, _last_ev_time_beats ( 0.0 )
, _last_ev_time_frames ( 0 )
2009-09-16 01:08:51 +00:00
, _smf_last_read_end ( 0 )
2010-05-24 15:42:27 +00:00
, _smf_last_read_time ( 0 )
2008-06-02 21:41:35 +00:00
{
2009-10-15 18:56:11 +00:00
if ( set_state ( node , Stateful : : loading_state_version ) ) {
2008-06-02 21:41:35 +00:00
throw failed_constructor ( ) ;
}
2009-10-14 16:10:01 +00:00
2014-04-14 03:03:35 -04:00
if ( init ( _path , true ) ) {
2008-06-02 21:41:35 +00:00
throw failed_constructor ( ) ;
}
2009-10-14 16:10:01 +00:00
2014-04-14 03:03:35 -04:00
assert ( Glib : : file_test ( _path , Glib : : FILE_TEST_EXISTS ) ) ;
existence_check ( ) ;
2008-09-30 00:45:26 +00:00
if ( open ( _path ) ) {
2008-06-02 21:41:35 +00:00
throw failed_constructor ( ) ;
}
2011-03-02 17:05:16 +00:00
2013-01-21 02:35:53 +00:00
_open = true ;
2008-06-02 21:41:35 +00:00
}
SMFSource : : ~ SMFSource ( )
{
if ( removable ( ) ) {
unlink ( _path . c_str ( ) ) ;
}
}
2011-03-02 17:05:16 +00:00
int
SMFSource : : open_for_write ( )
{
if ( create ( _path ) ) {
2013-01-21 02:35:53 +00:00
return - 1 ;
}
_open = true ;
return 0 ;
2011-03-02 17:05:16 +00:00
}
2008-06-02 21:41:35 +00:00
/** All stamps in audio frames */
2010-12-03 22:26:29 +00:00
framecnt_t
2013-01-21 02:35:53 +00:00
SMFSource : : read_unlocked ( Evoral : : EventSink < framepos_t > & destination ,
framepos_t const source_start ,
framepos_t start ,
framecnt_t duration ,
MidiStateTracker * tracker ) const
2008-06-02 21:41:35 +00:00
{
2009-02-17 02:11:49 +00:00
int ret = 0 ;
2008-06-02 21:41:35 +00:00
uint64_t time = 0 ; // in SMF ticks, 1 tick per _ppqn
2013-01-21 02:35:53 +00:00
if ( writable ( ) & & ! _open ) {
/* nothing to read since nothing has ben written */
return duration ;
}
2011-03-02 17:05:16 +00:00
2010-05-24 22:39:36 +00:00
DEBUG_TRACE ( DEBUG : : MidiSourceIO , string_compose ( " SMF read_unlocked: start %1 duration %2 \n " , start , duration ) ) ;
2008-06-02 21:41:35 +00:00
// Output parameters for read_event (which will allocate scratch in buffer as needed)
uint32_t ev_delta_t = 0 ;
2009-02-15 01:53:06 +00:00
uint32_t ev_type = 0 ;
uint32_t ev_size = 0 ;
uint8_t * ev_buffer = 0 ;
2008-06-02 21:41:35 +00:00
size_t scratch_size = 0 ; // keep track of scratch to minimize reallocs
2009-10-14 16:10:01 +00:00
2009-10-25 21:13:02 +00:00
BeatsFramesConverter converter ( _session . tempo_map ( ) , source_start ) ;
2008-06-02 21:41:35 +00:00
2009-02-19 04:12:54 +00:00
const uint64_t start_ticks = ( uint64_t ) ( converter . from ( start ) * ppqn ( ) ) ;
2010-05-24 22:39:36 +00:00
DEBUG_TRACE ( DEBUG : : MidiSourceIO , string_compose ( " SMF read_unlocked: start in ticks %1 \n " , start_ticks ) ) ;
2008-06-02 21:41:35 +00:00
2009-09-21 15:43:11 +00:00
if ( _smf_last_read_end = = 0 | | start ! = _smf_last_read_end ) {
2010-05-24 22:39:36 +00:00
DEBUG_TRACE ( DEBUG : : MidiSourceIO , string_compose ( " SMF read_unlocked: seek to %1 \n " , start ) ) ;
2009-02-15 02:09:58 +00:00
Evoral : : SMF : : seek_to_start ( ) ;
while ( time < start_ticks ) {
2010-12-20 22:51:17 +00:00
gint ignored ;
2010-07-20 16:27:34 +00:00
ret = read_event ( & ev_delta_t , & ev_size , & ev_buffer , & ignored ) ;
2009-02-15 02:09:58 +00:00
if ( ret = = - 1 ) { // EOF
2009-09-16 01:08:51 +00:00
_smf_last_read_end = start + duration ;
2009-04-14 10:13:06 +00:00
return duration ;
2009-02-15 02:09:58 +00:00
}
time + = ev_delta_t ; // accumulate delta time
}
2010-05-24 15:42:27 +00:00
} else {
2010-05-24 22:39:36 +00:00
DEBUG_TRACE ( DEBUG : : MidiSourceIO , string_compose ( " SMF read_unlocked: set time to %1 \n " , _smf_last_read_time ) ) ;
2010-05-24 15:42:27 +00:00
time = _smf_last_read_time ;
2009-09-21 15:43:11 +00:00
}
2009-10-14 16:10:01 +00:00
2009-09-16 01:08:51 +00:00
_smf_last_read_end = start + duration ;
2009-02-15 02:09:58 +00:00
2009-02-15 19:44:27 +00:00
while ( true ) {
2010-12-20 22:51:17 +00:00
gint ignored ; /* XXX don't ignore note id's ??*/
2010-07-20 16:27:34 +00:00
ret = read_event ( & ev_delta_t , & ev_size , & ev_buffer , & ignored ) ;
2008-06-02 21:41:35 +00:00
if ( ret = = - 1 ) { // EOF
break ;
}
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
time + = ev_delta_t ; // accumulate delta time
2010-05-24 15:42:27 +00:00
_smf_last_read_time = time ;
2008-06-02 21:41:35 +00:00
if ( ret = = 0 ) { // meta-event (skipped, just accumulate time)
continue ;
}
2009-10-14 16:10:01 +00:00
2009-02-15 20:50:20 +00:00
ev_type = EventTypeMap : : instance ( ) . midi_event_type ( ev_buffer [ 0 ] ) ;
2008-06-02 21:41:35 +00:00
2010-05-24 22:39:36 +00:00
DEBUG_TRACE ( DEBUG : : MidiSourceIO , string_compose ( " SMF read_unlocked delta %1, time %2, buf[0] %3, type %4 \n " ,
ev_delta_t , time , ev_buffer [ 0 ] , ev_type ) ) ;
2009-09-21 15:43:11 +00:00
2009-02-15 02:09:58 +00:00
assert ( time > = start_ticks ) ;
2008-06-02 21:41:35 +00:00
2010-09-01 01:50:24 +00:00
/* Note that we add on the source start time (in session frames) here so that ev_frame_time
is in session frames .
*/
2010-09-17 18:20:37 +00:00
const framepos_t ev_frame_time = converter . to ( time / ( double ) ppqn ( ) ) + source_start ;
2009-09-21 15:43:11 +00:00
2009-04-14 10:13:06 +00:00
if ( ev_frame_time < start + duration ) {
2010-09-01 01:50:24 +00:00
destination . write ( ev_frame_time , ev_type , ev_size , ev_buffer ) ;
2009-10-19 17:05:22 +00:00
if ( tracker ) {
if ( ev_buffer [ 0 ] & MIDI_CMD_NOTE_ON ) {
tracker - > add ( ev_buffer [ 1 ] , ev_buffer [ 0 ] & 0xf ) ;
} else if ( ev_buffer [ 0 ] & MIDI_CMD_NOTE_OFF ) {
tracker - > remove ( ev_buffer [ 1 ] , ev_buffer [ 0 ] & 0xf ) ;
}
}
2009-02-15 02:09:58 +00:00
} else {
break ;
2008-06-02 21:41:35 +00:00
}
2009-10-22 22:14:25 +00:00
2009-02-15 01:53:06 +00:00
if ( ev_size > scratch_size ) {
2008-06-02 21:41:35 +00:00
scratch_size = ev_size ;
2009-02-15 01:53:06 +00:00
}
2009-02-15 03:51:49 +00:00
ev_size = scratch_size ; // ensure read_event only allocates if necessary
2008-06-02 21:41:35 +00:00
}
2009-10-14 16:10:01 +00:00
2009-04-14 10:13:06 +00:00
return duration ;
2008-06-02 21:41:35 +00:00
}
2010-12-03 22:26:29 +00:00
framecnt_t
2013-01-21 02:35:53 +00:00
SMFSource : : write_unlocked ( MidiRingBuffer < framepos_t > & source ,
framepos_t position ,
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
framecnt_t cnt )
2008-06-02 21:41:35 +00:00
{
2013-01-21 02:35:53 +00:00
if ( ! _writing ) {
mark_streaming_write_started ( ) ;
}
2011-03-02 17:05:16 +00:00
2010-12-03 22:26:29 +00:00
framepos_t time ;
2009-04-16 14:34:56 +00:00
Evoral : : EventType type ;
uint32_t size ;
2008-06-02 21:41:35 +00:00
2009-02-15 01:53:06 +00:00
size_t buf_capacity = 4 ;
uint8_t * buf = ( uint8_t * ) malloc ( buf_capacity ) ;
2009-10-14 16:10:01 +00:00
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
if ( _model & & ! _model - > writing ( ) ) {
2008-06-02 21:41:35 +00:00
_model - > start_write ( ) ;
2009-02-15 01:53:06 +00:00
}
2008-06-02 21:41:35 +00:00
2010-12-03 22:26:29 +00:00
Evoral : : MIDIEvent < framepos_t > ev ;
2008-06-02 21:41:35 +00:00
while ( true ) {
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
/* Get the event time, in frames since session start but ignoring looping. */
2011-07-20 18:13:03 +00:00
bool ret ;
if ( ! ( ret = source . peek ( ( uint8_t * ) & time , sizeof ( time ) ) ) ) {
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
/* Ring is empty, no more events. */
2008-06-02 21:41:35 +00:00
break ;
2009-02-15 01:53:06 +00:00
}
2008-06-02 21:41:35 +00:00
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
if ( ( cnt ! = max_framecnt ) & &
( time > position + _capture_length + cnt ) ) {
/* The diskstream doesn't want us to write everything, and this
event is past the end of this block , so we ' re done for now . */
2011-07-20 18:13:03 +00:00
break ;
}
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
/* Read the time, type, and size of the event. */
2011-07-20 18:13:03 +00:00
if ( ! ( ret = source . read_prefix ( & time , & type , & size ) ) ) {
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
error < < _ ( " Unable to read event prefix, corrupt MIDI ring " ) < < endmsg ;
2008-06-02 21:41:35 +00:00
break ;
2009-02-15 01:53:06 +00:00
}
2008-06-02 21:41:35 +00:00
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
/* Enlarge body buffer if necessary now that we know the size. */
2009-04-16 14:34:56 +00:00
if ( size > buf_capacity ) {
buf_capacity = size ;
buf = ( uint8_t * ) realloc ( buf , size ) ;
2008-06-02 21:41:35 +00:00
}
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
/* Read the event body into buffer. */
2009-04-16 14:34:56 +00:00
ret = source . read_contents ( size , buf ) ;
2008-06-02 21:41:35 +00:00
if ( ! ret ) {
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
error < < _ ( " Event has time and size but no body, corrupt MIDI ring " ) < < endmsg ;
2008-06-02 21:41:35 +00:00
break ;
}
2009-10-14 16:10:01 +00:00
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
/* Convert event time from absolute to source relative. */
if ( time < position ) {
error < < _ ( " Event time is before MIDI source position " ) < < endmsg ;
break ;
}
2009-04-16 14:34:56 +00:00
time - = position ;
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
2009-04-16 14:34:56 +00:00
ev . set ( buf , size , time ) ;
2008-09-22 16:28:02 +00:00
ev . set_event_type ( EventTypeMap : : instance ( ) . midi_event_type ( ev . buffer ( ) [ 0 ] ) ) ;
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
ev . set_id ( Evoral : : next_event_id ( ) ) ;
2010-07-20 16:27:34 +00:00
2009-02-15 01:53:06 +00:00
if ( ! ( ev . is_channel_event ( ) | | ev . is_smf_meta_event ( ) | | ev . is_sysex ( ) ) ) {
2008-06-02 21:41:35 +00:00
continue ;
}
2009-10-14 16:10:01 +00:00
2009-02-19 04:12:54 +00:00
append_event_unlocked_frames ( ev , position ) ;
2008-06-02 21:41:35 +00:00
}
2012-07-17 03:10:40 +00:00
Evoral : : SMF : : flush ( ) ;
2011-07-14 12:59:51 +00:00
free ( buf ) ;
2008-06-02 21:41:35 +00:00
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
return cnt ;
2008-06-02 21:41:35 +00:00
}
2009-10-14 16:10:01 +00:00
2009-02-15 17:30:42 +00:00
/** Append an event with a timestamp in beats (double) */
2008-06-02 21:41:35 +00:00
void
2009-02-17 02:11:49 +00:00
SMFSource : : append_event_unlocked_beats ( const Evoral : : Event < double > & ev )
2008-06-02 21:41:35 +00:00
{
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
if ( ! _writing | | ev . size ( ) = = 0 ) {
2008-06-02 21:41:35 +00:00
return ;
2009-02-01 21:04:12 +00:00
}
2011-06-01 16:50:12 +00:00
2011-06-22 20:06:10 +00:00
/*printf("SMFSource: %s - append_event_unlocked_beats ID = %d time = %lf, size = %u, data = ",
2010-07-20 16:27:34 +00:00
name ( ) . c_str ( ) , ev . id ( ) , ev . time ( ) , ev . size ( ) ) ;
2011-06-22 20:06:10 +00:00
for ( size_t i = 0 ; i < ev . size ( ) ; + + i ) printf ( " %X " , ev . buffer ( ) [ i ] ) ; printf ( " \n " ) ; */
2009-10-14 16:10:01 +00:00
2009-02-15 17:30:42 +00:00
if ( ev . time ( ) < _last_ev_time_beats ) {
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
warning < < string_compose ( _ ( " Skipping event with unordered time %1 " ) , ev . time ( ) )
< < endmsg ;
2009-02-15 17:30:42 +00:00
return ;
}
2009-10-14 16:10:01 +00:00
2010-12-20 22:51:17 +00:00
Evoral : : event_id_t event_id ;
2010-07-20 16:27:34 +00:00
2010-12-20 22:51:17 +00:00
if ( ev . id ( ) < 0 ) {
event_id = Evoral : : next_event_id ( ) ;
} else {
event_id = ev . id ( ) ;
}
2010-07-20 16:27:34 +00:00
if ( _model ) {
_model - > append ( ev , event_id ) ;
}
2009-02-20 00:30:42 +00:00
_length_beats = max ( _length_beats , ev . time ( ) ) ;
2009-10-14 16:10:01 +00:00
2009-02-15 17:30:42 +00:00
const double delta_time_beats = ev . time ( ) - _last_ev_time_beats ;
const uint32_t delta_time_ticks = ( uint32_t ) lrint ( delta_time_beats * ( double ) ppqn ( ) ) ;
2009-10-14 16:10:01 +00:00
2010-07-20 16:27:34 +00:00
Evoral : : SMF : : append_event_delta ( delta_time_ticks , ev . size ( ) , ev . buffer ( ) , event_id ) ;
2009-02-15 17:30:42 +00:00
_last_ev_time_beats = ev . time ( ) ;
}
2010-12-03 22:26:29 +00:00
/** Append an event with a timestamp in frames (framepos_t) */
2009-02-15 17:30:42 +00:00
void
2010-12-03 22:26:29 +00:00
SMFSource : : append_event_unlocked_frames ( const Evoral : : Event < framepos_t > & ev , framepos_t position )
2009-02-15 17:30:42 +00:00
{
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
if ( ! _writing | | ev . size ( ) = = 0 ) {
2008-06-02 21:41:35 +00:00
return ;
}
2009-02-15 17:30:42 +00:00
2012-07-17 03:10:40 +00:00
// printf("SMFSource: %s - append_event_unlocked_frames ID = %d time = %u, size = %u, data = ",
// name().c_str(), ev.id(), ev.time(), ev.size());
// for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
2009-10-14 16:10:01 +00:00
2009-02-15 17:30:42 +00:00
if ( ev . time ( ) < _last_ev_time_frames ) {
Fix MIDI loop recording.
This changes how things work a bit, but I am committing it for 3.0 since the
previous revision often crashed (and never worked), this one seems to work
fine, and the code is quite a bit more cogent.
I have tested the following use cases with live input and audible output:
* Non-loop recording, armed before roll
* Non-loop recording, arm while rolling
* Loop recording, armed before roll
* Loop recording, arm during roll
In the last case, the source/region is created starting at the loop start
rather than the current transport frame as usual so time makes sense when it
wraps around.
See the documentation added to the code for details, but the basic idea here is
to simply push MIDI events to the source with increasing monotonic time,
ignoring looping altogether. Essentially we pretend the loop does not exist,
but the source knows all the details so it can implement whatever behaviour is
appropriate.
Currently, this is simply recording a complete non-destructive copy of the
input, which is a good thing. Perhaps not what the user expects of loop
recording, but at least it works and is one sensible option. We will need to
add more later.
Display while recording is a little bit wacky, but whatever.
git-svn-id: svn://localhost/ardour2/branches/3.0@13940 d708f5d6-7413-0410-9779-e7cbd77b26cf
2013-01-21 06:00:15 +00:00
warning < < string_compose ( _ ( " Skipping event with unordered time %1 " ) , ev . time ( ) )
< < endmsg ;
2009-02-15 17:30:42 +00:00
return ;
}
2011-03-01 02:04:34 +00:00
2009-10-25 21:13:02 +00:00
BeatsFramesConverter converter ( _session . tempo_map ( ) , position ) ;
2010-12-20 22:51:17 +00:00
const double ev_time_beats = converter . from ( ev . time ( ) ) ;
Evoral : : event_id_t event_id ;
2010-07-20 16:27:34 +00:00
2010-12-20 22:51:17 +00:00
if ( ev . id ( ) < 0 ) {
event_id = Evoral : : next_event_id ( ) ;
} else {
event_id = ev . id ( ) ;
}
2010-07-20 16:27:34 +00:00
if ( _model ) {
2011-06-01 16:50:12 +00:00
const Evoral : : Event < double > beat_ev ( ev . event_type ( ) ,
ev_time_beats ,
ev . size ( ) ,
2012-08-10 15:57:09 +00:00
const_cast < uint8_t * > ( ev . buffer ( ) ) ) ;
2010-07-20 16:27:34 +00:00
_model - > append ( beat_ev , event_id ) ;
2011-06-01 16:50:12 +00:00
}
2009-10-14 16:10:01 +00:00
2010-07-20 16:27:34 +00:00
_length_beats = max ( _length_beats , ev_time_beats ) ;
2009-10-14 16:10:01 +00:00
2011-12-10 13:48:50 +00:00
const Evoral : : MusicalTime last_time_beats = converter . from ( _last_ev_time_frames ) ;
const Evoral : : MusicalTime delta_time_beats = ev_time_beats - last_time_beats ;
const uint32_t delta_time_ticks = ( uint32_t ) ( lrint ( delta_time_beats * ( double ) ppqn ( ) ) ) ;
2008-06-02 21:41:35 +00:00
2010-07-20 16:27:34 +00:00
Evoral : : SMF : : append_event_delta ( delta_time_ticks , ev . size ( ) , ev . buffer ( ) , event_id ) ;
2009-02-15 17:30:42 +00:00
_last_ev_time_frames = ev . time ( ) ;
2008-06-02 21:41:35 +00:00
}
XMLNode &
SMFSource : : get_state ( )
{
2010-12-20 22:51:17 +00:00
XMLNode & node = MidiSource : : get_state ( ) ;
node . add_property ( X_ ( " origin " ) , _origin ) ;
return node ;
2008-06-02 21:41:35 +00:00
}
int
2009-10-15 00:57:55 +00:00
SMFSource : : set_state ( const XMLNode & node , int version )
2008-06-02 21:41:35 +00:00
{
2009-10-15 18:56:11 +00:00
if ( Source : : set_state ( node , version ) ) {
2009-02-17 02:11:49 +00:00
return - 1 ;
}
2008-06-02 21:41:35 +00:00
2009-10-15 18:56:11 +00:00
if ( MidiSource : : set_state ( node , version ) ) {
2008-06-02 21:41:35 +00:00
return - 1 ;
}
2009-10-14 16:10:01 +00:00
2009-10-15 18:56:11 +00:00
if ( FileSource : : set_state ( node , version ) ) {
2009-02-17 02:11:49 +00:00
return - 1 ;
2008-06-02 21:41:35 +00:00
}
return 0 ;
}
void
2011-03-02 17:05:16 +00:00
SMFSource : : mark_streaming_midi_write_started ( NoteMode mode )
2008-06-02 21:41:35 +00:00
{
2013-01-21 02:35:53 +00:00
/* CALLER MUST HOLD LOCK */
2011-03-02 17:05:16 +00:00
2013-01-21 02:35:53 +00:00
if ( ! _open & & open_for_write ( ) ) {
error < < string_compose ( _ ( " cannot open MIDI file %1 for write " ) , _path ) < < endmsg ;
/* XXX should probably throw or return something */
return ;
}
2011-03-03 03:54:28 +00:00
2011-03-02 17:05:16 +00:00
MidiSource : : mark_streaming_midi_write_started ( mode ) ;
2009-02-15 01:32:41 +00:00
Evoral : : SMF : : begin_write ( ) ;
2009-02-15 17:30:42 +00:00
_last_ev_time_beats = 0.0 ;
_last_ev_time_frames = 0 ;
2008-06-02 21:41:35 +00:00
}
void
SMFSource : : mark_streaming_write_completed ( )
2011-07-20 18:13:03 +00:00
{
mark_midi_streaming_write_completed ( Evoral : : Sequence < Evoral : : MusicalTime > : : DeleteStuckNotes ) ;
}
void
SMFSource : : mark_midi_streaming_write_completed ( Evoral : : Sequence < Evoral : : MusicalTime > : : StuckNoteOption stuck_notes_option , Evoral : : MusicalTime when )
2008-06-02 21:41:35 +00:00
{
2012-07-25 17:48:55 +00:00
Glib : : Threads : : Mutex : : Lock lm ( _lock ) ;
2011-07-20 18:13:03 +00:00
MidiSource : : mark_midi_streaming_write_completed ( stuck_notes_option , when ) ;
2008-06-02 21:41:35 +00:00
if ( ! writable ( ) ) {
2012-07-17 03:10:40 +00:00
warning < < string_compose ( " attempt to write to unwritable SMF file %1 " , _path ) < < endmsg ;
2008-06-02 21:41:35 +00:00
return ;
}
2009-10-14 16:10:01 +00:00
2010-06-28 01:12:21 +00:00
if ( _model ) {
_model - > set_edited ( false ) ;
}
2011-06-01 16:50:12 +00:00
2009-02-15 01:32:41 +00:00
Evoral : : SMF : : end_write ( ) ;
2010-07-01 18:54:19 +00:00
2010-12-20 22:51:17 +00:00
/* data in the file now, not removable */
2010-07-01 18:54:19 +00:00
2011-06-01 16:50:12 +00:00
mark_nonremovable ( ) ;
2008-06-02 21:41:35 +00:00
}
bool
2010-09-14 15:45:21 +00:00
SMFSource : : safe_midi_file_extension ( const string & file )
2008-06-02 21:41:35 +00:00
{
2013-01-16 17:09:52 +00:00
static regex_t compiled_pattern ;
static bool compile = true ;
const int nmatches = 2 ;
regmatch_t matches [ nmatches ] ;
2014-02-27 20:37:17 -05:00
if ( Glib : : file_test ( file , Glib : : FILE_TEST_EXISTS ) ) {
if ( ! Glib : : file_test ( file , Glib : : FILE_TEST_IS_REGULAR ) ) {
/* exists but is not a regular file */
return false ;
}
}
if ( compile & & regcomp ( & compiled_pattern , " \\ .[mM][iI][dD][iI]?$ " , REG_EXTENDED ) ) {
2013-01-16 17:09:52 +00:00
return false ;
} else {
compile = false ;
}
if ( regexec ( & compiled_pattern , file . c_str ( ) , nmatches , matches , 0 ) ) {
return false ;
}
return true ;
2008-06-02 21:41:35 +00:00
}
2014-01-20 13:44:31 +01:00
static bool compare_eventlist (
const std : : pair < Evoral : : Event < double > * , gint > & a ,
const std : : pair < Evoral : : Event < double > * , gint > & b ) {
return ( a . first - > time ( ) < b . first - > time ( ) ) ;
}
2008-06-02 21:41:35 +00:00
void
2009-02-17 02:11:49 +00:00
SMFSource : : load_model ( bool lock , bool force_reload )
2008-06-02 21:41:35 +00:00
{
2009-01-28 04:55:31 +00:00
if ( _writing ) {
2008-06-02 21:41:35 +00:00
return ;
2009-01-28 04:55:31 +00:00
}
2009-10-14 16:10:01 +00:00
2012-07-25 17:48:55 +00:00
boost : : shared_ptr < Glib : : Threads : : Mutex : : Lock > lm ;
2009-10-22 21:03:47 +00:00
if ( lock )
2012-07-25 17:48:55 +00:00
lm = boost : : shared_ptr < Glib : : Threads : : Mutex : : Lock > ( new Glib : : Threads : : Mutex : : Lock ( _lock ) ) ;
2008-06-02 21:41:35 +00:00
2009-02-15 19:44:27 +00:00
if ( _model & & ! force_reload ) {
2008-06-02 21:41:35 +00:00
return ;
2009-01-28 04:55:31 +00:00
}
2008-06-02 21:41:35 +00:00
2010-12-09 21:34:31 +00:00
if ( ! _model ) {
_model = boost : : shared_ptr < MidiModel > ( new MidiModel ( shared_from_this ( ) ) ) ;
2008-06-02 21:41:35 +00:00
} else {
_model - > clear ( ) ;
}
2013-01-21 02:35:53 +00:00
if ( writable ( ) & & ! _open ) {
return ;
}
2011-03-02 17:05:16 +00:00
2008-06-02 21:41:35 +00:00
_model - > start_write ( ) ;
2009-02-15 01:32:41 +00:00
Evoral : : SMF : : seek_to_start ( ) ;
2008-06-02 21:41:35 +00:00
uint64_t time = 0 ; /* in SMF ticks */
2009-02-02 02:36:05 +00:00
Evoral : : Event < double > ev ;
2009-10-14 16:10:01 +00:00
2011-10-19 18:11:31 +00:00
uint32_t scratch_size = 0 ; // keep track of scratch and minimize reallocs
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
uint32_t delta_t = 0 ;
uint32_t size = 0 ;
uint8_t * buf = NULL ;
int ret ;
2010-12-20 22:51:17 +00:00
gint event_id ;
2014-01-20 13:44:31 +01:00
bool have_event_id ;
2010-07-20 16:27:34 +00:00
2014-01-20 13:44:31 +01:00
// TODO simplify event allocation
std : : list < std : : pair < Evoral : : Event < double > * , gint > > eventlist ;
2011-06-01 16:50:12 +00:00
2014-01-20 13:44:31 +01:00
for ( unsigned i = 1 ; i < = num_tracks ( ) ; + + i ) {
if ( seek_to_track ( i ) ) continue ;
2010-07-20 16:27:34 +00:00
2014-01-20 13:44:31 +01:00
time = 0 ;
have_event_id = false ;
2010-07-20 16:27:34 +00:00
2014-01-20 13:44:31 +01:00
while ( ( ret = read_event ( & delta_t , & size , & buf , & event_id ) ) > = 0 ) {
2011-06-01 16:50:12 +00:00
2014-01-20 13:44:31 +01:00
time + = delta_t ;
2010-07-20 16:27:34 +00:00
2014-01-20 13:44:31 +01:00
if ( ret = = 0 ) {
/* meta-event : did we get an event ID ? */
if ( event_id > = 0 ) {
have_event_id = true ;
}
continue ;
}
2009-09-14 16:01:32 +00:00
2014-01-20 13:44:31 +01:00
if ( ret > 0 ) {
/* not a meta-event */
2010-05-30 20:13:29 +00:00
2014-01-20 13:44:31 +01:00
if ( ! have_event_id ) {
event_id = Evoral : : next_event_id ( ) ;
}
uint32_t event_type = EventTypeMap : : instance ( ) . midi_event_type ( buf [ 0 ] ) ;
double event_time = time / ( double ) ppqn ( ) ;
2010-06-01 20:40:45 +00:00
# ifndef NDEBUG
2014-01-20 13:44:31 +01:00
std : : string ss ;
2011-06-01 16:50:12 +00:00
2014-01-20 13:44:31 +01:00
for ( uint32_t xx = 0 ; xx < size ; + + xx ) {
char b [ 8 ] ;
snprintf ( b , sizeof ( b ) , " 0x%x " , buf [ xx ] ) ;
ss + = b ;
}
2010-12-20 22:51:17 +00:00
2014-01-20 13:44:31 +01:00
DEBUG_TRACE ( DEBUG : : MidiSourceIO , string_compose ( " SMF %6 load model delta %1, time %2, size %3 buf %4, type %5 \n " ,
delta_t , time , size , ss , event_type , name ( ) ) ) ;
2010-06-01 20:40:45 +00:00
# endif
2011-06-01 16:50:12 +00:00
2014-01-20 13:44:31 +01:00
eventlist . push_back ( make_pair (
new Evoral : : Event < double > (
event_type , event_time ,
size , buf , true )
, event_id ) ) ;
2008-06-02 21:41:35 +00:00
2014-01-20 13:44:31 +01:00
// Set size to max capacity to minimize allocs in read_event
scratch_size = std : : max ( size , scratch_size ) ;
size = scratch_size ;
2011-06-01 16:50:12 +00:00
2014-01-20 13:44:31 +01:00
_length_beats = max ( _length_beats , event_time ) ;
}
/* event ID's must immediately precede the event they are for */
have_event_id = false ;
2010-12-20 22:51:17 +00:00
}
2014-01-20 13:44:31 +01:00
}
2009-10-14 16:10:01 +00:00
2014-01-20 13:44:31 +01:00
eventlist . sort ( compare_eventlist ) ;
2011-06-01 16:50:12 +00:00
2014-01-20 13:44:31 +01:00
std : : list < std : : pair < Evoral : : Event < double > * , gint > > : : iterator it ;
for ( it = eventlist . begin ( ) ; it ! = eventlist . end ( ) ; + + it ) {
_model - > append ( * it - > first , it - > second ) ;
delete it - > first ;
2009-01-28 04:55:31 +00:00
}
2011-07-20 18:13:03 +00:00
_model - > end_write ( Evoral : : Sequence < Evoral : : MusicalTime > : : ResolveStuckNotes , _length_beats ) ;
2011-07-14 12:59:51 +00:00
_model - > set_edited ( false ) ;
2009-01-29 00:18:26 +00:00
2009-02-15 22:10:32 +00:00
_model_iter = _model - > begin ( ) ;
2009-01-29 00:18:26 +00:00
free ( buf ) ;
}
2008-06-02 21:41:35 +00:00
void
2009-02-17 02:11:49 +00:00
SMFSource : : destroy_model ( )
2008-06-02 21:41:35 +00:00
{
//cerr << _name << " destroying model " << _model.get() << endl;
_model . reset ( ) ;
}
2008-09-30 00:45:26 +00:00
void
2009-02-17 02:11:49 +00:00
SMFSource : : flush_midi ( )
2008-09-30 00:45:26 +00:00
{
2011-03-02 17:05:16 +00:00
if ( ! writable ( ) | | ( writable ( ) & & ! _open ) ) {
2010-12-20 22:51:17 +00:00
return ;
}
2010-06-26 13:45:59 +00:00
2011-07-14 12:59:51 +00:00
Evoral : : SMF : : end_write ( ) ;
2010-12-20 22:51:17 +00:00
/* data in the file means its no longer removable */
2011-06-01 16:50:12 +00:00
mark_nonremovable ( ) ;
2008-09-30 00:45:26 +00:00
}
2010-07-16 14:55:11 +00:00
void
SMFSource : : set_path ( const string & p )
{
2010-12-20 22:51:17 +00:00
FileSource : : set_path ( p ) ;
SMF : : set_path ( _path ) ;
2010-07-16 14:55:11 +00:00
}
2011-05-08 23:45:33 +00:00
/** Ensure that this source has some file on disk, even if it's just a SMF header */
void
SMFSource : : ensure_disk_file ( )
{
if ( _model ) {
/* We have a model, so write it to disk; see MidiSource::session_saved
for an explanation of what we are doing here .
*/
boost : : shared_ptr < MidiModel > mm = _model ;
_model . reset ( ) ;
mm - > sync_to_source ( ) ;
_model = mm ;
} else {
/* No model; if it's not already open, it's an empty source, so create
and open it for writing .
*/
if ( ! _open ) {
open_for_write ( ) ;
}
/* Flush, which will definitely put something on disk */
flush_midi ( ) ;
}
}
2014-04-04 15:26:44 -04:00
void
SMFSource : : prevent_deletion ( )
{
/* Unlike the audio case, the MIDI file remains mutable (because we can
edit MIDI data )
*/
_flags = Flag ( _flags & ~ ( Removable | RemovableIfEmpty | RemoveAtDestroy ) ) ;
}
2014-04-13 11:12:22 -04:00
int
SMFSource : : rename ( const string & newname )
{
Glib : : Threads : : Mutex : : Lock lm ( _lock ) ;
string oldpath = _path ;
string newpath = _session . new_source_path_from_name ( DataType : : MIDI , newname ) ;
if ( newpath . empty ( ) ) {
error < < string_compose ( _ ( " programming error: %1 " ) , " cannot generate a changed file path " ) < < endmsg ;
return - 1 ;
}
// Test whether newpath exists, if yes notify the user but continue.
if ( Glib : : file_test ( newpath , Glib : : FILE_TEST_EXISTS ) ) {
error < < string_compose ( _ ( " Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers. " ) , PROGRAM_NAME ) < < endmsg ;
return - 1 ;
}
if ( Glib : : file_test ( oldpath . c_str ( ) , Glib : : FILE_TEST_EXISTS ) ) {
/* rename only needed if file exists on disk */
if ( : : rename ( oldpath . c_str ( ) , newpath . c_str ( ) ) ! = 0 ) {
error < < string_compose ( _ ( " cannot rename file %1 to %2 (%3) " ) , oldpath , newpath , strerror ( errno ) ) < < endmsg ;
return - 1 ;
}
}
_name = Glib : : path_get_basename ( newpath ) ;
_path = newpath ;
return 0 ;
}