Preliminary (read: kludgey) MIDI import support.
Only really works when tracks contain only channel 1 data for now. git-svn-id: svn://localhost/ardour2/branches/3.0@3083 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
1b2fe7bf34
commit
fbfb26b45c
|
@ -156,10 +156,11 @@ style "default_button"
|
|||
font_name = "%FONT_SMALL%"
|
||||
fg[ACTIVE] = { 1.0, 1.0, 1.0 }
|
||||
|
||||
bg[NORMAL] = { 0.31, 0.35, 0.39 }
|
||||
bg[NORMAL] = { 0.33, 0.37, 0.42 }
|
||||
bg[ACTIVE] = "#565690"
|
||||
bg[PRELIGHT] = { 0.41, 0.45, 0.49 }
|
||||
bg[INSENSITIVE] = { 0.11, 0.15, 0.19 }
|
||||
bg[PRELIGHT] = { 0.49, 0.53, 0.59 }
|
||||
bg[INSENSITIVE] = { 0.31, 0.35, 0.36 }
|
||||
fg[INSENSITIVE] = { 0.61, 0.65, 0.67 }
|
||||
bg[SELECTED] = { 0.11, 0.15, 0.19 }
|
||||
}
|
||||
|
||||
|
|
|
@ -2090,7 +2090,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
|
|||
|
||||
edit_items.push_back (SeparatorElem());
|
||||
edit_items.push_back (MenuElem (_("Insert Selected Region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
|
||||
edit_items.push_back (MenuElem (_("Insert Existing Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
|
||||
edit_items.push_back (MenuElem (_("Insert Existing Media"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
|
||||
|
||||
/* Nudge track */
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include <ardour/stretch.h>
|
||||
#include <ardour/location.h>
|
||||
#include <ardour/audioregion.h>
|
||||
#include <ardour/track.h>
|
||||
|
||||
#include "audio_clock.h"
|
||||
#include "gtk-custom-ruler.h"
|
||||
|
@ -1139,16 +1140,17 @@ class Editor : public PublicEditor
|
|||
bool idle_do_embed (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, nframes64_t&);
|
||||
|
||||
int import_sndfiles (vector<Glib::ustring> paths, Editing::ImportMode mode, ARDOUR::SrcQuality, nframes64_t& pos,
|
||||
int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&, bool);
|
||||
int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::Track>&, bool);
|
||||
int embed_sndfiles (vector<Glib::ustring> paths, bool multiple_files, bool& check_sample_rate, Editing::ImportMode mode,
|
||||
nframes64_t& pos, int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&);
|
||||
nframes64_t& pos, int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::Track>&);
|
||||
|
||||
int add_sources (vector<Glib::ustring> paths, ARDOUR::SourceList& sources, nframes64_t& pos, Editing::ImportMode,
|
||||
int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&, bool add_channel_suffix);
|
||||
int finish_bringing_in_audio (boost::shared_ptr<ARDOUR::AudioRegion> region, uint32_t, uint32_t, nframes64_t& pos, Editing::ImportMode mode,
|
||||
boost::shared_ptr<ARDOUR::AudioTrack>& existing_track);
|
||||
int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::Track>&, bool add_channel_suffix);
|
||||
int finish_bringing_in_material (boost::shared_ptr<ARDOUR::Region> region, uint32_t, uint32_t, nframes64_t& pos, Editing::ImportMode mode,
|
||||
boost::shared_ptr<ARDOUR::Track>& existing_track);
|
||||
|
||||
boost::shared_ptr<ARDOUR::AudioTrack> get_nth_selected_audio_track (int nth) const;
|
||||
boost::shared_ptr<ARDOUR::MidiTrack> get_nth_selected_midi_track (int nth) const;
|
||||
|
||||
/* generic interthread progress window */
|
||||
|
||||
|
|
|
@ -529,9 +529,9 @@ Editor::register_actions ()
|
|||
|
||||
/* the next two are duplicate items with different names for use in two different contexts */
|
||||
|
||||
ActionManager::register_action (editor_actions, X_("addExistingAudioFiles"), _("Add Existing Audio"), mem_fun (*this, &Editor::external_audio_dialog));
|
||||
ActionManager::register_action (editor_actions, X_("addExistingAudioFiles"), _("Add Existing Media"), mem_fun (*this, &Editor::external_audio_dialog));
|
||||
|
||||
act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Add External Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion));
|
||||
act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Add External Media"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion));
|
||||
ActionManager::session_sensitive_actions.push_back (act);
|
||||
|
||||
ActionManager::register_toggle_action (editor_actions, X_("ToggleWaveformVisibility"), _("Show Waveforms"), mem_fun (*this, &Editor::toggle_waveform_visibility));
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
#include <ardour/audioplaylist.h>
|
||||
#include <ardour/audioregion.h>
|
||||
#include <ardour/audio_diskstream.h>
|
||||
#include <ardour/midi_track.h>
|
||||
#include <ardour/midi_region.h>
|
||||
#include <ardour/utils.h>
|
||||
#include <ardour/audio_track.h>
|
||||
#include <ardour/audioplaylist.h>
|
||||
|
@ -51,6 +53,7 @@
|
|||
#include "sfdb_ui.h"
|
||||
#include "editing.h"
|
||||
#include "audio_time_axis.h"
|
||||
#include "midi_time_axis.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
@ -70,13 +73,13 @@ void
|
|||
Editor::add_external_audio_action (ImportMode mode_hint)
|
||||
{
|
||||
if (session == 0) {
|
||||
MessageDialog msg (0, _("You can't import or embed an audiofile until you have a session loaded."));
|
||||
MessageDialog msg (0, _("You can't import or embed a file until you have a session loaded."));
|
||||
msg.run ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (sfbrowser == 0) {
|
||||
sfbrowser = new SoundFileOmega (*this, _("Add existing audio"), session, 0, true, mode_hint);
|
||||
sfbrowser = new SoundFileOmega (*this, _("Add existing media"), session, 0, true, mode_hint);
|
||||
} else {
|
||||
sfbrowser->set_mode (mode_hint);
|
||||
}
|
||||
|
@ -91,7 +94,7 @@ Editor::external_audio_dialog ()
|
|||
uint32_t track_cnt;
|
||||
|
||||
if (session == 0) {
|
||||
MessageDialog msg (0, _("You can't import or embed an audiofile until you have a session loaded."));
|
||||
MessageDialog msg (0, _("You can't import or embed a file until you have a session loaded."));
|
||||
msg.run ();
|
||||
return;
|
||||
}
|
||||
|
@ -109,7 +112,7 @@ Editor::external_audio_dialog ()
|
|||
}
|
||||
|
||||
if (sfbrowser == 0) {
|
||||
sfbrowser = new SoundFileOmega (*this, _("Add existing audio"), session, track_cnt, true);
|
||||
sfbrowser = new SoundFileOmega (*this, _("Add existing media"), session, track_cnt, true);
|
||||
} else {
|
||||
sfbrowser->reset (track_cnt);
|
||||
}
|
||||
|
@ -203,9 +206,7 @@ Editor::check_whether_and_how_to_import(string path, bool all_or_nothing)
|
|||
bool wave_name_exists = false;
|
||||
|
||||
for (SourceMap::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
|
||||
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
|
||||
|
||||
string tmp (Glib::path_get_basename (afs->path()));
|
||||
string tmp (Glib::path_get_basename (i->second->path()));
|
||||
|
||||
if (tmp == wave_name) {
|
||||
wave_name_exists = true;
|
||||
|
@ -281,10 +282,40 @@ Editor::get_nth_selected_audio_track (int nth) const
|
|||
return atv->audio_track();
|
||||
}
|
||||
|
||||
boost::shared_ptr<MidiTrack>
|
||||
Editor::get_nth_selected_midi_track (int nth) const
|
||||
{
|
||||
MidiTimeAxisView* mtv;
|
||||
TrackSelection::iterator x;
|
||||
|
||||
for (x = selection->tracks.begin(); nth > 0 && x != selection->tracks.end(); ++x) {
|
||||
|
||||
mtv = dynamic_cast<MidiTimeAxisView*>(*x);
|
||||
|
||||
if (!mtv) {
|
||||
continue;
|
||||
} else if (mtv->is_midi_track()) {
|
||||
--nth;
|
||||
}
|
||||
}
|
||||
|
||||
if (x == selection->tracks.end()) {
|
||||
mtv = dynamic_cast<MidiTimeAxisView*>(selection->tracks.back());
|
||||
} else {
|
||||
mtv = dynamic_cast<MidiTimeAxisView*>(*x);
|
||||
}
|
||||
|
||||
if (!mtv || !mtv->is_midi_track()) {
|
||||
return boost::shared_ptr<MidiTrack>();
|
||||
}
|
||||
|
||||
return mtv->midi_track();
|
||||
}
|
||||
|
||||
void
|
||||
Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mode, SrcQuality quality, nframes64_t& pos)
|
||||
{
|
||||
boost::shared_ptr<AudioTrack> track;
|
||||
boost::shared_ptr<Track> track;
|
||||
vector<ustring> to_import;
|
||||
bool ok = true;
|
||||
int nth = 0;
|
||||
|
@ -409,7 +440,7 @@ Editor::do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode
|
|||
void
|
||||
Editor::_do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos)
|
||||
{
|
||||
boost::shared_ptr<AudioTrack> track;
|
||||
boost::shared_ptr<Track> track;
|
||||
bool check_sample_rate = true;
|
||||
bool ok = false;
|
||||
vector<ustring> to_embed;
|
||||
|
@ -474,7 +505,7 @@ Editor::_do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mod
|
|||
|
||||
int
|
||||
Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality quality, nframes64_t& pos,
|
||||
int target_regions, int target_tracks, boost::shared_ptr<AudioTrack>& track, bool replace)
|
||||
int target_regions, int target_tracks, boost::shared_ptr<Track>& track, bool replace)
|
||||
{
|
||||
WindowTitle title = string_compose (_("importing %1"), paths.front());
|
||||
|
||||
|
@ -535,7 +566,7 @@ Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality qual
|
|||
int
|
||||
Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
|
||||
bool& check_sample_rate, ImportMode mode, nframes64_t& pos, int target_regions, int target_tracks,
|
||||
boost::shared_ptr<AudioTrack>& track)
|
||||
boost::shared_ptr<Track>& track)
|
||||
{
|
||||
boost::shared_ptr<AudioFileSource> source;
|
||||
SourceList sources;
|
||||
|
@ -688,9 +719,9 @@ Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
|
|||
|
||||
int
|
||||
Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64_t& pos, ImportMode mode,
|
||||
int target_regions, int target_tracks, boost::shared_ptr<AudioTrack>& track, bool add_channel_suffix)
|
||||
int target_regions, int target_tracks, boost::shared_ptr<Track>& track, bool add_channel_suffix)
|
||||
{
|
||||
vector<boost::shared_ptr<AudioRegion> > regions;
|
||||
vector<boost::shared_ptr<Region> > regions;
|
||||
ustring region_name;
|
||||
uint32_t input_chan = 0;
|
||||
uint32_t output_chan = 0;
|
||||
|
@ -703,15 +734,16 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
|
|||
}
|
||||
}
|
||||
|
||||
cout << "TARGET REGIONS: " << target_regions << endl;
|
||||
|
||||
if (target_regions == 1) {
|
||||
|
||||
/* take all the sources we have and package them up as a region */
|
||||
|
||||
region_name = region_name_from_path (paths.front(), (sources.size() > 1), false);
|
||||
|
||||
regions.push_back (boost::dynamic_pointer_cast<AudioRegion>
|
||||
(RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0,
|
||||
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External))));
|
||||
regions.push_back (RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0,
|
||||
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)));
|
||||
|
||||
} else if (target_regions == -1) {
|
||||
|
||||
|
@ -726,13 +758,10 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
|
|||
just_one.clear ();
|
||||
just_one.push_back (*x);
|
||||
|
||||
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*x);
|
||||
region_name = region_name_from_path ((*x)->path(), false, true, sources.size(), n);
|
||||
|
||||
region_name = region_name_from_path (afs->path(), false, true, sources.size(), n);
|
||||
|
||||
regions.push_back (boost::dynamic_pointer_cast<AudioRegion>
|
||||
(RegionFactory::create (just_one, 0, (*x)->length(), region_name, 0,
|
||||
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External))));
|
||||
regions.push_back (RegionFactory::create (just_one, 0, (*x)->length(), region_name, 0,
|
||||
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)));
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -755,9 +784,9 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
|
|||
|
||||
int n = 0;
|
||||
|
||||
for (vector<boost::shared_ptr<AudioRegion> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
|
||||
for (vector<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
|
||||
|
||||
finish_bringing_in_audio (*r, input_chan, output_chan, pos, mode, track);
|
||||
finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track);
|
||||
|
||||
if (target_tracks != 1) {
|
||||
track.reset ();
|
||||
|
@ -776,9 +805,12 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
|
|||
}
|
||||
|
||||
int
|
||||
Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_t in_chans, uint32_t out_chans, nframes64_t& pos,
|
||||
ImportMode mode, boost::shared_ptr<AudioTrack>& existing_track)
|
||||
Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t in_chans, uint32_t out_chans, nframes64_t& pos,
|
||||
ImportMode mode, boost::shared_ptr<Track>& existing_track)
|
||||
{
|
||||
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
|
||||
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region);
|
||||
|
||||
switch (mode) {
|
||||
case ImportAsRegion:
|
||||
/* relax, its been done */
|
||||
|
@ -788,7 +820,11 @@ Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_
|
|||
{
|
||||
if (!existing_track) {
|
||||
|
||||
existing_track = get_nth_selected_audio_track (0);
|
||||
if (ar) {
|
||||
existing_track = get_nth_selected_audio_track (0);
|
||||
} else if (mr) {
|
||||
existing_track = get_nth_selected_midi_track (0);
|
||||
}
|
||||
|
||||
if (!existing_track) {
|
||||
return -1;
|
||||
|
@ -796,8 +832,8 @@ Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_
|
|||
}
|
||||
|
||||
boost::shared_ptr<Playlist> playlist = existing_track->diskstream()->playlist();
|
||||
boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
|
||||
begin_reversible_command (_("insert sndfile"));
|
||||
boost::shared_ptr<Region> copy (RegionFactory::create (region));
|
||||
begin_reversible_command (_("insert file"));
|
||||
XMLNode &before = playlist->get_state();
|
||||
playlist->add_region (copy, pos);
|
||||
session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
|
||||
|
@ -808,17 +844,28 @@ Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_
|
|||
case ImportAsTrack:
|
||||
{
|
||||
if (!existing_track) {
|
||||
list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Normal, 1));
|
||||
if (ar) {
|
||||
list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Normal, 1));
|
||||
|
||||
if (at.empty()) {
|
||||
return -1;
|
||||
if (at.empty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
existing_track = at.front();
|
||||
} else if (mr) {
|
||||
list<boost::shared_ptr<MidiTrack> > mt (session->new_midi_track (Normal, 1));
|
||||
|
||||
if (mt.empty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
existing_track = mt.front();
|
||||
}
|
||||
|
||||
existing_track = at.front();
|
||||
existing_track->set_name (region->name());
|
||||
}
|
||||
|
||||
boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
|
||||
boost::shared_ptr<Region> copy (RegionFactory::create (region));
|
||||
existing_track->diskstream()->playlist()->add_region (copy, pos);
|
||||
break;
|
||||
}
|
||||
|
@ -826,9 +873,12 @@ Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_
|
|||
|
||||
case ImportAsTapeTrack:
|
||||
{
|
||||
if (!ar)
|
||||
return -1;
|
||||
|
||||
list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Destructive));
|
||||
if (!at.empty()) {
|
||||
boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
|
||||
boost::shared_ptr<Region> copy (RegionFactory::create (region));
|
||||
at.front()->set_name (basename_nosuffix (copy->name()));
|
||||
at.front()->diskstream()->playlist()->add_region (copy, pos);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace Canvas {
|
|||
|
||||
LinesetClass Lineset::lineset_class;
|
||||
|
||||
static const char* overlap_error_str = "Lineset error: Line overlap";
|
||||
//static const char* overlap_error_str = "Lineset error: Line overlap";
|
||||
|
||||
Lineset::Line::Line(double c, double w, uint32_t color)
|
||||
: coord(c)
|
||||
|
@ -142,7 +142,7 @@ Lineset::change_line_width(double coord, double width) {
|
|||
|
||||
if(it != lines.end()) {
|
||||
if(l.coord + width > it->coord) {
|
||||
cerr << overlap_error_str << endl;
|
||||
//cerr << overlap_error_str << endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -171,14 +171,14 @@ Lineset::add_line(double coord, double width, uint32_t color) {
|
|||
/* overlap checking */
|
||||
if(it != lines.end()) {
|
||||
if(l.coord + l.width > it->coord) {
|
||||
cerr << overlap_error_str << endl;
|
||||
//cerr << overlap_error_str << endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(it != lines.begin()) {
|
||||
--it;
|
||||
if(l.coord < it->coord + it->width) {
|
||||
cerr << overlap_error_str << endl;
|
||||
//cerr << overlap_error_str << endl;
|
||||
return;
|
||||
}
|
||||
++it;
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
#include <ardour/auditioner.h>
|
||||
#include <ardour/audioregion.h>
|
||||
#include <ardour/audiofilesource.h>
|
||||
#include <ardour/smf_source.h>
|
||||
#include <ardour/smf_reader.h>
|
||||
#include <ardour/region_factory.h>
|
||||
#include <ardour/source_factory.h>
|
||||
#include <ardour/session.h>
|
||||
|
@ -433,13 +435,17 @@ SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::S
|
|||
found_list_view.get_selection()->set_mode (SELECTION_MULTIPLE);
|
||||
found_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
|
||||
|
||||
custom_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_custom));
|
||||
custom_filter.set_name (_("Audio files"));
|
||||
audio_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_audio_filter));
|
||||
audio_filter.set_name (_("Audio files"));
|
||||
|
||||
midi_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_midi_filter));
|
||||
midi_filter.set_name (_("MIDI files"));
|
||||
|
||||
matchall_filter.add_pattern ("*.*");
|
||||
matchall_filter.set_name (_("All files"));
|
||||
|
||||
chooser.add_filter (custom_filter);
|
||||
chooser.add_filter (audio_filter);
|
||||
chooser.add_filter (midi_filter);
|
||||
chooser.add_filter (matchall_filter);
|
||||
chooser.set_select_multiple (true);
|
||||
chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
|
||||
|
@ -552,11 +558,17 @@ SoundFileBrowser::meter ()
|
|||
}
|
||||
|
||||
bool
|
||||
SoundFileBrowser::on_custom (const FileFilter::Info& filter_info)
|
||||
SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
|
||||
{
|
||||
return AudioFileSource::safe_file_extension (filter_info.filename);
|
||||
}
|
||||
|
||||
bool
|
||||
SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
|
||||
{
|
||||
return SMFSource::safe_file_extension (filter_info.filename);
|
||||
}
|
||||
|
||||
void
|
||||
SoundFileBrowser::update_preview ()
|
||||
{
|
||||
|
@ -894,6 +906,14 @@ SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool&
|
|||
src_needed = true;
|
||||
}
|
||||
|
||||
} else if (SMFSource::safe_file_extension (*i)) {
|
||||
SMFReader reader(*i);
|
||||
if (reader.num_tracks() > 1) {
|
||||
cout << *i << " MULTI CHANNEL" << endl;
|
||||
multichannel = true;
|
||||
} else {
|
||||
cout << *i << " SINGLE CHANNEL" << endl;
|
||||
}
|
||||
} else {
|
||||
err = true;
|
||||
}
|
||||
|
|
|
@ -132,7 +132,8 @@ class SoundFileBrowser : public ArdourDialog
|
|||
protected:
|
||||
bool resetting_ourselves;
|
||||
|
||||
Gtk::FileFilter custom_filter;
|
||||
Gtk::FileFilter audio_filter;
|
||||
Gtk::FileFilter midi_filter;
|
||||
Gtk::FileFilter matchall_filter;
|
||||
SoundFileBox preview;
|
||||
Gtk::HBox hpacker;
|
||||
|
@ -159,7 +160,8 @@ class SoundFileBrowser : public ArdourDialog
|
|||
|
||||
void chooser_file_activated ();
|
||||
|
||||
bool on_custom (const Gtk::FileFilter::Info& filter_info);
|
||||
bool on_audio_filter (const Gtk::FileFilter::Info& filter_info);
|
||||
bool on_midi_filter (const Gtk::FileFilter::Info& filter_info);
|
||||
|
||||
virtual bool reset_options() { return true; }
|
||||
|
||||
|
|
|
@ -129,6 +129,7 @@ session_time.cc
|
|||
session_transport.cc
|
||||
session_utils.cc
|
||||
silentfilesource.cc
|
||||
smf_reader.cc
|
||||
smf_source.cc
|
||||
sndfile_helpers.cc
|
||||
sndfilesource.cc
|
||||
|
|
|
@ -86,7 +86,7 @@ struct MidiEvent {
|
|||
if (_owns_buffer) {
|
||||
if (copy._buffer) {
|
||||
if (!_buffer || _size < copy._size)
|
||||
_buffer = (Byte*)realloc(_buffer, copy._size);
|
||||
_buffer = (Byte*)::realloc(_buffer, copy._size);
|
||||
memcpy(_buffer, copy._buffer, copy._size);
|
||||
} else {
|
||||
free(_buffer);
|
||||
|
@ -130,6 +130,11 @@ struct MidiEvent {
|
|||
_owns_buffer = own;
|
||||
}
|
||||
|
||||
inline void realloc(size_t size) {
|
||||
assert(_owns_buffer);
|
||||
_buffer = (Byte*) ::realloc(_buffer, size);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
inline void set_buffer(Byte* buf) { _buffer = buf; }
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace ARDOUR {
|
|||
/** Return the size of the given event NOT including the status byte,
|
||||
* or -1 if unknown (eg sysex)
|
||||
*/
|
||||
int
|
||||
static inline int
|
||||
midi_event_size(unsigned char status)
|
||||
{
|
||||
if (status >= 0x80 && status <= 0xE0) {
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Written by Dave Robillard
|
||||
|
||||
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 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_smf_reader_h__
|
||||
#define __ardour_smf_reader_h__
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <inttypes.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
|
||||
/** Standard MIDI File (Type 0) Reader
|
||||
*
|
||||
* Currently this only reads SMF files with tempo-based timing.
|
||||
*/
|
||||
class SMFReader {
|
||||
public:
|
||||
class PrematureEOF : public std::exception {
|
||||
const char* what() const throw() { return "Unexpected end of file"; }
|
||||
};
|
||||
class CorruptFile : public std::exception {
|
||||
const char* what() const throw() { return "Corrupted file"; }
|
||||
};
|
||||
class UnsupportedTime : public std::exception {
|
||||
const char* what() const throw() { return "Unsupported time stamp type (SMPTE)"; }
|
||||
};
|
||||
|
||||
SMFReader(const std::string filename="");
|
||||
~SMFReader();
|
||||
|
||||
bool open(const std::string& filename) throw (std::logic_error, UnsupportedTime);
|
||||
|
||||
bool seek_to_track(unsigned track) throw (std::logic_error);
|
||||
|
||||
const std::string& filename() const { return _filename; };
|
||||
|
||||
//TimeUnit unit() const { return _unit; }
|
||||
uint16_t type() const { return _type; }
|
||||
uint16_t ppqn() const { return _ppqn; }
|
||||
uint16_t num_tracks() const { return _num_tracks; }
|
||||
|
||||
int read_event(size_t buf_len,
|
||||
uint8_t* buf,
|
||||
uint32_t* ev_size,
|
||||
uint32_t* ev_delta_time)
|
||||
throw (std::logic_error, PrematureEOF, CorruptFile);
|
||||
|
||||
void close();
|
||||
|
||||
protected:
|
||||
/** size of SMF header, including MTrk chunk header */
|
||||
static const uint32_t HEADER_SIZE = 22;
|
||||
|
||||
uint32_t read_var_len() const throw(PrematureEOF);
|
||||
|
||||
std::string _filename;
|
||||
FILE* _fd;
|
||||
//TimeUnit _unit;
|
||||
uint16_t _type;
|
||||
uint16_t _ppqn;
|
||||
uint16_t _num_tracks;
|
||||
uint32_t _track;
|
||||
uint32_t _track_size;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_smf_reader_h__ */
|
||||
|
|
@ -63,8 +63,10 @@ class SMFSource : public MidiSource {
|
|||
|
||||
bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); }
|
||||
int set_source_name (string newname, bool destructive);
|
||||
|
||||
static bool safe_file_extension (const Glib::ustring& path);
|
||||
|
||||
string path() const { return _path; }
|
||||
Glib::ustring path() const { return _path; }
|
||||
|
||||
void set_allow_remove_if_empty (bool yn);
|
||||
void mark_for_remove();
|
||||
|
@ -120,7 +122,7 @@ class SMFSource : public MidiSource {
|
|||
static const uint16_t _ppqn = 19200;
|
||||
|
||||
uint16_t _channel;
|
||||
string _path;
|
||||
Glib::ustring _path;
|
||||
Flag _flags;
|
||||
string _take_id;
|
||||
bool _allow_remove_if_empty;
|
||||
|
|
|
@ -50,8 +50,9 @@ class Source : public SessionObject, public ARDOUR::Readable
|
|||
time_t timestamp() const { return _timestamp; }
|
||||
void stamp (time_t when) { _timestamp = when; }
|
||||
|
||||
/** @return the number of items in this source */
|
||||
nframes_t length() const { return _length; }
|
||||
|
||||
virtual Glib::ustring path() const = 0;
|
||||
|
||||
virtual nframes_t natural_position() const { return 0; }
|
||||
|
||||
|
@ -88,10 +89,10 @@ class Source : public SessionObject, public ARDOUR::Readable
|
|||
AnalysisFeatureList transients;
|
||||
std::string get_transients_path() const;
|
||||
int load_transients (const std::string&);
|
||||
|
||||
void update_length (nframes_t pos, nframes_t cnt);
|
||||
|
||||
protected:
|
||||
void update_length (nframes_t pos, nframes_t cnt);
|
||||
|
||||
DataType _type;
|
||||
time_t _timestamp;
|
||||
nframes_t _length;
|
||||
|
|
|
@ -39,7 +39,9 @@
|
|||
|
||||
#include <ardour/ardour.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/session_directory.h>
|
||||
#include <ardour/audio_diskstream.h>
|
||||
#include <ardour/audioengine.h>
|
||||
#include <ardour/sndfilesource.h>
|
||||
#include <ardour/sndfile_helpers.h>
|
||||
#include <ardour/audioregion.h>
|
||||
|
@ -47,6 +49,9 @@
|
|||
#include <ardour/source_factory.h>
|
||||
#include <ardour/resampled_source.h>
|
||||
#include <ardour/analyser.h>
|
||||
#include <ardour/smf_reader.h>
|
||||
#include <ardour/smf_source.h>
|
||||
#include <ardour/tempo.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
|
@ -66,23 +71,25 @@ open_importable_source (const string& path, nframes_t samplerate, ARDOUR::SrcQua
|
|||
}
|
||||
|
||||
static std::string
|
||||
get_non_existent_filename (const bool allow_replacing, const std::string destdir, const std::string& basename, uint channel, uint channels)
|
||||
get_non_existent_filename (DataType type, const bool allow_replacing, const std::string destdir, const std::string& basename, uint channel, uint channels)
|
||||
{
|
||||
char buf[PATH_MAX+1];
|
||||
bool goodfile = false;
|
||||
string base(basename);
|
||||
const char* ext = (type == DataType::AUDIO) ? "wav" : "mid";
|
||||
|
||||
do {
|
||||
if (channels == 2) {
|
||||
|
||||
if (type == DataType::AUDIO && channels == 2) {
|
||||
if (channel == 0) {
|
||||
snprintf (buf, sizeof(buf), "%s-L.wav", base.c_str());
|
||||
} else {
|
||||
snprintf (buf, sizeof(buf), "%s-R.wav", base.c_str());
|
||||
}
|
||||
} else if (channels > 1) {
|
||||
snprintf (buf, sizeof(buf), "%s-c%d.wav", base.c_str(), channel+1);
|
||||
snprintf (buf, sizeof(buf), "%s-c%d.%s", base.c_str(), channel, ext);
|
||||
} else {
|
||||
snprintf (buf, sizeof(buf), "%s.wav", base.c_str());
|
||||
snprintf (buf, sizeof(buf), "%s.%s", base.c_str(), ext);
|
||||
}
|
||||
|
||||
|
||||
|
@ -112,13 +119,20 @@ get_paths_for_new_sources (const bool allow_replacing, const string& import_file
|
|||
vector<string> new_paths;
|
||||
const string basename = basename_nosuffix (import_file_path);
|
||||
|
||||
SessionDirectory sdir(session_dir);
|
||||
|
||||
for (uint n = 0; n < channels; ++n) {
|
||||
|
||||
std::string filepath;
|
||||
const DataType type = (import_file_path.rfind(".mid") != string::npos)
|
||||
? DataType::MIDI : DataType::AUDIO;
|
||||
|
||||
std::string filepath = (type == DataType::MIDI)
|
||||
? sdir.midi_path().to_string() : sdir.sound_path().to_string();
|
||||
|
||||
filepath = session_dir;
|
||||
filepath += '/';
|
||||
filepath += get_non_existent_filename (allow_replacing, session_dir, basename, n, channels);
|
||||
filepath += get_non_existent_filename (type, allow_replacing, filepath, basename, n, channels);
|
||||
|
||||
cout << "NEW SOURCE PATH: " << filepath << endl;
|
||||
|
||||
new_paths.push_back (filepath);
|
||||
}
|
||||
|
@ -128,7 +142,7 @@ get_paths_for_new_sources (const bool allow_replacing, const string& import_file
|
|||
|
||||
static bool
|
||||
map_existing_mono_sources (const vector<string>& new_paths, Session& sess,
|
||||
uint samplerate, vector<boost::shared_ptr<AudioFileSource> >& newfiles, Session *session)
|
||||
uint samplerate, vector<boost::shared_ptr<Source> >& newfiles, Session *session)
|
||||
{
|
||||
for (vector<string>::const_iterator i = new_paths.begin();
|
||||
i != new_paths.end(); ++i)
|
||||
|
@ -140,14 +154,14 @@ map_existing_mono_sources (const vector<string>& new_paths, Session& sess,
|
|||
return false;
|
||||
}
|
||||
|
||||
newfiles.push_back(boost::dynamic_pointer_cast<AudioFileSource>(source));
|
||||
newfiles.push_back(boost::dynamic_pointer_cast<Source>(source));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
|
||||
uint samplerate, vector<boost::shared_ptr<AudioFileSource> >& newfiles)
|
||||
uint samplerate, vector<boost::shared_ptr<Source> >& newfiles)
|
||||
{
|
||||
for (vector<string>::const_iterator i = new_paths.begin();
|
||||
i != new_paths.end(); ++i)
|
||||
|
@ -156,13 +170,16 @@ create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
|
|||
|
||||
try
|
||||
{
|
||||
const DataType type = ((*i).rfind(".mid") != string::npos)
|
||||
? DataType::MIDI : DataType::AUDIO;
|
||||
|
||||
source = SourceFactory::createWritable (
|
||||
DataType::AUDIO,
|
||||
sess,
|
||||
i->c_str(),
|
||||
false, // destructive
|
||||
samplerate
|
||||
);
|
||||
type,
|
||||
sess,
|
||||
i->c_str(),
|
||||
false, // destructive
|
||||
samplerate
|
||||
);
|
||||
}
|
||||
catch (const failed_constructor& err)
|
||||
{
|
||||
|
@ -170,7 +187,7 @@ create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
|
|||
return false;
|
||||
}
|
||||
|
||||
newfiles.push_back(boost::dynamic_pointer_cast<AudioFileSource>(source));
|
||||
newfiles.push_back(boost::dynamic_pointer_cast<Source>(source));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -197,9 +214,10 @@ compose_status_message (const string& path,
|
|||
|
||||
static void
|
||||
write_audio_data_to_new_files (ImportableSource* source, Session::import_status& status,
|
||||
vector<boost::shared_ptr<AudioFileSource> >& newfiles)
|
||||
vector<boost::shared_ptr<Source> >& newfiles)
|
||||
{
|
||||
const nframes_t nframes = ResampledImportableSource::blocksize;
|
||||
boost::shared_ptr<AudioFileSource> afs;
|
||||
uint channels = source->channels();
|
||||
|
||||
boost::scoped_array<float> data(new float[nframes * channels]);
|
||||
|
@ -236,7 +254,9 @@ write_audio_data_to_new_files (ImportableSource* source, Session::import_status&
|
|||
/* flush to disk */
|
||||
|
||||
for (chn = 0; chn < channels; ++chn) {
|
||||
newfiles[chn]->write (channel_data[chn].get(), nfread);
|
||||
if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(newfiles[chn])) != 0) {
|
||||
afs->write (channel_data[chn].get(), nfread);
|
||||
}
|
||||
}
|
||||
|
||||
read_count += nread;
|
||||
|
@ -245,9 +265,71 @@ write_audio_data_to_new_files (ImportableSource* source, Session::import_status&
|
|||
}
|
||||
|
||||
static void
|
||||
remove_file_source (boost::shared_ptr<AudioFileSource> file_source)
|
||||
write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
|
||||
vector<boost::shared_ptr<Source> >& newfiles)
|
||||
{
|
||||
::unlink (file_source->path().c_str());
|
||||
MidiEvent ev(0.0, 4, NULL, true);
|
||||
|
||||
uint64_t t = 0;
|
||||
uint32_t delta_t = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
status.progress = 0.0f;
|
||||
|
||||
try {
|
||||
|
||||
for (unsigned i = 1; i <= source->num_tracks(); ++i) {
|
||||
|
||||
boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(newfiles[i-1]);
|
||||
|
||||
source->seek_to_track(i);
|
||||
|
||||
while (!status.cancel) {
|
||||
|
||||
if (source->read_event(4, ev.buffer(), &size, &delta_t) < 0)
|
||||
break;
|
||||
|
||||
// FIXME: kluuudge
|
||||
if (ev.channel() != 0) {
|
||||
cout << "Skipping event with channel " << ev.channel() << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
t += delta_t;
|
||||
ev.time() = t * (double)source->ppqn();
|
||||
ev.size() = size;
|
||||
|
||||
smfs->append_event_unlocked(ev);
|
||||
if (status.progress < 0.99)
|
||||
status.progress += 0.01;
|
||||
}
|
||||
|
||||
nframes_t timeline_position = 0; // FIXME: ?
|
||||
|
||||
// FIXME: kluuuuudge: assumes tempo never changes after start
|
||||
const double frames_per_beat = smfs->session().tempo_map().tempo_at(
|
||||
timeline_position).frames_per_beat(
|
||||
smfs->session().engine().frame_rate(),
|
||||
smfs->session().tempo_map().meter_at(timeline_position));
|
||||
|
||||
smfs->update_length(0, (t * source->ppqn()) * frames_per_beat);
|
||||
|
||||
smfs->flush_header();
|
||||
smfs->flush_footer();
|
||||
|
||||
if (status.cancel)
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
error << "Corrupt MIDI file " << source->filename() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
remove_file_source (boost::shared_ptr<Source> source)
|
||||
{
|
||||
::unlink (source->path().c_str());
|
||||
}
|
||||
|
||||
// This function is still unable to cleanly update an existing source, even though
|
||||
|
@ -258,8 +340,10 @@ void
|
|||
Session::import_audiofiles (import_status& status)
|
||||
{
|
||||
uint32_t cnt = 1;
|
||||
typedef vector<boost::shared_ptr<AudioFileSource> > AudioSources;
|
||||
AudioSources all_new_sources;
|
||||
typedef vector<boost::shared_ptr<Source> > Sources;
|
||||
Sources all_new_sources;
|
||||
boost::shared_ptr<AudioFileSource> afs;
|
||||
uint channels = 0;
|
||||
|
||||
status.sources.clear ();
|
||||
|
||||
|
@ -268,24 +352,44 @@ Session::import_audiofiles (import_status& status)
|
|||
++p, ++cnt)
|
||||
{
|
||||
std::auto_ptr<ImportableSource> source;
|
||||
std::auto_ptr<SMFReader> smf_reader;
|
||||
|
||||
const DataType type = ((*p).rfind(".mid") != string::npos)
|
||||
? DataType::MIDI : DataType::AUDIO;
|
||||
|
||||
try
|
||||
{
|
||||
source = open_importable_source (*p, frame_rate(), status.quality);
|
||||
}
|
||||
catch (const failed_constructor& err)
|
||||
{
|
||||
error << string_compose(_("Import: cannot open input sound file \"%1\""), (*p)) << endmsg;
|
||||
status.done = status.cancel = true;
|
||||
return;
|
||||
if (type == DataType::AUDIO) {
|
||||
try
|
||||
{
|
||||
source = open_importable_source (*p, frame_rate(), status.quality);
|
||||
channels = source->channels();
|
||||
} catch (const failed_constructor& err)
|
||||
{
|
||||
error << string_compose(_("Import: cannot open input sound file \"%1\""), (*p)) << endmsg;
|
||||
status.done = status.cancel = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try
|
||||
{
|
||||
smf_reader = std::auto_ptr<SMFReader>(new SMFReader(*p));
|
||||
channels = smf_reader->num_tracks();
|
||||
} catch (const SMFReader::UnsupportedTime& err) {
|
||||
error << _("Import: unsupported MIDI time stamp format") << endmsg;
|
||||
status.done = status.cancel = true;
|
||||
return;
|
||||
} catch (...) {
|
||||
error << _("Import: error reading MIDI file") << endmsg;
|
||||
status.done = status.cancel = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vector<string> new_paths = get_paths_for_new_sources (status.replace_existing_source,
|
||||
*p,
|
||||
get_best_session_directory_for_new_source (),
|
||||
source->channels());
|
||||
channels);
|
||||
|
||||
AudioSources newfiles;
|
||||
Sources newfiles;
|
||||
|
||||
if (status.replace_existing_source) {
|
||||
fatal << "THIS IS NOT IMPLEMENTED YET, IT SHOULD NEVER GET CALLED!!! DYING!" << endl;
|
||||
|
@ -299,14 +403,20 @@ Session::import_audiofiles (import_status& status)
|
|||
|
||||
if (status.cancel) break;
|
||||
|
||||
for (AudioSources::iterator i = newfiles.begin(); i != newfiles.end(); ++i) {
|
||||
(*i)->prepare_for_peakfile_writes ();
|
||||
for (Sources::iterator i = newfiles.begin(); i != newfiles.end(); ++i) {
|
||||
if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(*i)) != 0) {
|
||||
afs->prepare_for_peakfile_writes ();
|
||||
}
|
||||
}
|
||||
|
||||
status.doing_what = compose_status_message (*p, source->samplerate(),
|
||||
frame_rate(), cnt, status.paths.size());
|
||||
|
||||
write_audio_data_to_new_files (source.get(), status, newfiles);
|
||||
if (source.get()) { // audio
|
||||
status.doing_what = compose_status_message (*p, source->samplerate(),
|
||||
frame_rate(), cnt, status.paths.size());
|
||||
write_audio_data_to_new_files (source.get(), status, newfiles);
|
||||
} else if (smf_reader.get()) { // midi
|
||||
status.doing_what = string_compose(_("loading MIDI file %1"), *p);
|
||||
write_midi_data_to_new_files (smf_reader.get(), status, newfiles);
|
||||
}
|
||||
}
|
||||
|
||||
if (!status.cancel) {
|
||||
|
@ -318,10 +428,14 @@ Session::import_audiofiles (import_status& status)
|
|||
|
||||
/* flush the final length(s) to the header(s) */
|
||||
|
||||
for (AudioSources::iterator x = all_new_sources.begin(); x != all_new_sources.end(); ++x)
|
||||
for (Sources::iterator x = all_new_sources.begin(); x != all_new_sources.end(); ++x)
|
||||
{
|
||||
(*x)->update_header(0, *now, xnow);
|
||||
(*x)->done_with_peakfile_writes ();
|
||||
cout << "NEW SOURCE: " << (*x)->path() << endl;
|
||||
|
||||
if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(*x)) != 0) {
|
||||
afs->update_header(0, *now, xnow);
|
||||
afs->done_with_peakfile_writes ();
|
||||
}
|
||||
|
||||
/* now that there is data there, requeue the file for analysis */
|
||||
|
||||
|
|
|
@ -670,7 +670,7 @@ MidiDiskstream::set_pending_overwrite (bool yn)
|
|||
int
|
||||
MidiDiskstream::overwrite_existing_buffers ()
|
||||
{
|
||||
cerr << "MDS: overwrite_existing_buffers() (does nothing)" << endl;
|
||||
//cerr << "MDS: overwrite_existing_buffers() (does nothing)" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -433,7 +433,7 @@ MidiModel::append_note_off_unlocked(double time, uint8_t note_num)
|
|||
Note& note = *_notes[*n].get();
|
||||
//cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl;
|
||||
if (note.note() == note_num) {
|
||||
assert(time > note.time());
|
||||
assert(time >= note.time());
|
||||
note.set_duration(time - note.time());
|
||||
_write_notes.erase(n);
|
||||
//cerr << "MM resolved note, duration: " << note.duration() << endl;
|
||||
|
|
|
@ -522,6 +522,8 @@ Playlist::set_region_ownership ()
|
|||
void
|
||||
Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
|
||||
{
|
||||
assert(region->data_type() == _type);
|
||||
|
||||
RegionSortByPosition cmp;
|
||||
nframes_t old_length = 0;
|
||||
|
||||
|
|
|
@ -108,6 +108,8 @@ RegionFactory::create (Session& session, XMLNode& node, bool yn)
|
|||
boost::shared_ptr<Region>
|
||||
RegionFactory::create (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags, bool announce)
|
||||
{
|
||||
cerr << "CREATE REGION " << name << " " << start << " * " << length << endl;
|
||||
|
||||
if (srcs.empty()) {
|
||||
return boost::shared_ptr<Region>();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
Copyright (C) 2008 Paul Davis
|
||||
Written by Dave Robillard
|
||||
|
||||
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 02139, USA.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <glibmm/miscutils.h>
|
||||
#include <ardour/smf_reader.h>
|
||||
#include <ardour/midi_events.h>
|
||||
#include <ardour/midi_util.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
|
||||
SMFReader::SMFReader(const std::string filename)
|
||||
: _fd(NULL)
|
||||
//, _unit(TimeUnit::BEATS, 192)
|
||||
, _ppqn(0)
|
||||
, _track(0)
|
||||
, _track_size(0)
|
||||
{
|
||||
if (filename.length() > 0) {
|
||||
open(filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SMFReader::~SMFReader()
|
||||
{
|
||||
if (_fd)
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SMFReader::open(const string& filename) throw (logic_error, UnsupportedTime)
|
||||
{
|
||||
if (_fd)
|
||||
throw logic_error("Attempt to start new read while write in progress.");
|
||||
|
||||
cout << "Opening SMF file " << filename << " for reading." << endl;
|
||||
|
||||
_fd = fopen(filename.c_str(), "r+");
|
||||
|
||||
if (_fd) {
|
||||
// Read type (bytes 8..9)
|
||||
fseek(_fd, 0, SEEK_SET);
|
||||
char mthd[5];
|
||||
mthd[4] = '\0';
|
||||
fread(mthd, 1, 4, _fd);
|
||||
if (strcmp(mthd, "MThd")) {
|
||||
cerr << filename << " is not an SMF file, aborting." << endl;
|
||||
fclose(_fd);
|
||||
_fd = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read type (bytes 8..9)
|
||||
fseek(_fd, 8, SEEK_SET);
|
||||
uint16_t type_be = 0;
|
||||
fread(&type_be, 2, 1, _fd);
|
||||
_type = GUINT16_FROM_BE(type_be);
|
||||
|
||||
// Read number of tracks (bytes 10..11)
|
||||
uint16_t num_tracks_be = 0;
|
||||
fread(&num_tracks_be, 2, 1, _fd);
|
||||
_num_tracks = GUINT16_FROM_BE(num_tracks_be);
|
||||
|
||||
// Read PPQN (bytes 12..13)
|
||||
|
||||
uint16_t ppqn_be = 0;
|
||||
fread(&ppqn_be, 2, 1, _fd);
|
||||
_ppqn = GUINT16_FROM_BE(ppqn_be);
|
||||
|
||||
// TODO: Absolute (SMPTE seconds) time support
|
||||
if ((_ppqn & 0x8000) != 0)
|
||||
throw UnsupportedTime();
|
||||
|
||||
//_unit = TimeUnit::beats(_ppqn);
|
||||
|
||||
seek_to_track(1);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Seek to the start of a given track, starting from 1.
|
||||
* Returns true if specified track was found.
|
||||
*/
|
||||
bool
|
||||
SMFReader::seek_to_track(unsigned track) throw (std::logic_error)
|
||||
{
|
||||
if (track == 0)
|
||||
throw logic_error("Seek to track 0 out of range (must be >= 1)");
|
||||
|
||||
if (!_fd)
|
||||
throw logic_error("Attempt to seek to track on unopened SMF file.");
|
||||
|
||||
unsigned track_pos = 0;
|
||||
|
||||
fseek(_fd, 14, SEEK_SET);
|
||||
char id[5];
|
||||
id[4] = '\0';
|
||||
uint32_t chunk_size = 0;
|
||||
|
||||
while (!feof(_fd)) {
|
||||
fread(id, 1, 4, _fd);
|
||||
|
||||
if (!strcmp(id, "MTrk")) {
|
||||
++track_pos;
|
||||
//std::cerr << "Found track " << track_pos << endl;
|
||||
} else {
|
||||
std::cerr << "Unknown chunk ID " << id << endl;
|
||||
}
|
||||
|
||||
uint32_t chunk_size_be;
|
||||
fread(&chunk_size_be, 4, 1, _fd);
|
||||
chunk_size = GUINT32_FROM_BE(chunk_size_be);
|
||||
|
||||
if (track_pos == track)
|
||||
break;
|
||||
|
||||
fseek(_fd, chunk_size, SEEK_CUR);
|
||||
}
|
||||
|
||||
if (!feof(_fd) && track_pos == track) {
|
||||
_track = track;
|
||||
_track_size = chunk_size;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Read an event from the current position in file.
|
||||
*
|
||||
* File position MUST be at the beginning of a delta time, or this will die very messily.
|
||||
* ev.buffer must be of size ev.size, and large enough for the event. The returned event
|
||||
* will have it's time field set to it's delta time (so it's the caller's responsibility
|
||||
* to keep track of delta time, even for ignored events).
|
||||
*
|
||||
* Returns event length (including status byte) on success, 0 if event was
|
||||
* skipped (eg a meta event), or -1 on EOF (or end of track).
|
||||
*
|
||||
* If @a buf is not large enough to hold the event, 0 will be returned, but ev_size
|
||||
* set to the actual size of the event.
|
||||
*/
|
||||
int
|
||||
SMFReader::read_event(size_t buf_len,
|
||||
uint8_t* buf,
|
||||
uint32_t* ev_size,
|
||||
uint32_t* delta_time)
|
||||
throw (std::logic_error, PrematureEOF, CorruptFile)
|
||||
{
|
||||
if (_track == 0)
|
||||
throw logic_error("Attempt to read from unopened SMF file");
|
||||
|
||||
if (!_fd || feof(_fd)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(buf_len > 0);
|
||||
assert(buf);
|
||||
assert(ev_size);
|
||||
assert(delta_time);
|
||||
|
||||
//cerr.flags(ios::hex);
|
||||
//cerr << "SMF - Reading event at offset 0x" << ftell(_fd) << endl;
|
||||
//cerr.flags(ios::dec);
|
||||
|
||||
// Running status state
|
||||
static uint8_t last_status = 0;
|
||||
static uint32_t last_size = 0;
|
||||
|
||||
*delta_time = read_var_len();
|
||||
int status = fgetc(_fd);
|
||||
if (status == EOF)
|
||||
throw PrematureEOF();
|
||||
else if (status > 0xFF)
|
||||
throw CorruptFile();
|
||||
|
||||
if (status < 0x80) {
|
||||
if (last_status == 0)
|
||||
throw CorruptFile();
|
||||
status = last_status;
|
||||
*ev_size = last_size;
|
||||
fseek(_fd, -1, SEEK_CUR);
|
||||
//cerr << "RUNNING STATUS, size = " << *ev_size << endl;
|
||||
} else {
|
||||
last_status = status;
|
||||
*ev_size = midi_event_size(status) + 1;
|
||||
last_size = *ev_size;
|
||||
//cerr << "NORMAL STATUS, size = " << *ev_size << endl;
|
||||
}
|
||||
|
||||
buf[0] = (uint8_t)status;
|
||||
|
||||
if (status == 0xFF) {
|
||||
*ev_size = 0;
|
||||
if (feof(_fd))
|
||||
throw PrematureEOF();
|
||||
uint8_t type = fgetc(_fd);
|
||||
const uint32_t size = read_var_len();
|
||||
/*cerr.flags(ios::hex);
|
||||
cerr << "SMF - meta 0x" << (int)type << ", size = ";
|
||||
cerr.flags(ios::dec);
|
||||
cerr << size << endl;*/
|
||||
|
||||
if ((uint8_t)type == 0x2F) {
|
||||
//cerr << "SMF - hit EOT" << endl;
|
||||
return -1; // we hit the logical EOF anyway...
|
||||
} else {
|
||||
fseek(_fd, size, SEEK_CUR);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (*ev_size > buf_len || *ev_size == 0 || feof(_fd)) {
|
||||
//cerr << "Skipping event" << endl;
|
||||
// Skip event, return 0
|
||||
fseek(_fd, *ev_size - 1, SEEK_CUR);
|
||||
return 0;
|
||||
} else {
|
||||
// Read event, return size
|
||||
if (ferror(_fd))
|
||||
throw CorruptFile();
|
||||
fread(buf+1, 1, *ev_size - 1, _fd);
|
||||
|
||||
if ((buf[0] & 0xF0) == 0x90 && buf[2] == 0) {
|
||||
buf[0] = (0x80 | (buf[0] & 0x0F));
|
||||
buf[2] = 0x40;
|
||||
}
|
||||
|
||||
return *ev_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SMFReader::close()
|
||||
{
|
||||
if (_fd)
|
||||
fclose(_fd);
|
||||
|
||||
_fd = NULL;
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
SMFReader::read_var_len() const throw(PrematureEOF)
|
||||
{
|
||||
if (feof(_fd))
|
||||
throw PrematureEOF();
|
||||
|
||||
uint32_t value;
|
||||
uint8_t c;
|
||||
|
||||
if ( (value = getc(_fd)) & 0x80 ) {
|
||||
value &= 0x7F;
|
||||
do {
|
||||
if (feof(_fd))
|
||||
throw PrematureEOF();
|
||||
value = (value << 7) + ((c = getc(_fd)) & 0x7F);
|
||||
} while (c & 0x80);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
|
@ -70,7 +70,7 @@ SMFSource::SMFSource (Session& s, std::string path, Flag flags)
|
|||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
//cerr << "SMF Source path: " << path << endl;
|
||||
cerr << "SMF Source path: " << path << endl;
|
||||
|
||||
assert(_name.find("/") == string::npos);
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ SMFSource::SMFSource (Session& s, const XMLNode& node)
|
|||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
//cerr << "SMF Source name: " << _name << endl;
|
||||
cerr << "SMF Source name: " << _name << endl;
|
||||
|
||||
assert(_name.find("/") == string::npos);
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ SMFSource::flush_header ()
|
|||
|
||||
const uint16_t type = GUINT16_TO_BE(0); // SMF Type 0 (single track)
|
||||
const uint16_t ntracks = GUINT16_TO_BE(1); // Number of tracks (always 1 for Type 0)
|
||||
const uint16_t division = GUINT16_TO_BE(_ppqn); // Pulses per beat
|
||||
const uint16_t division = GUINT16_TO_BE(_ppqn); // Pulses per quarter note (beat)
|
||||
|
||||
char data[6];
|
||||
memcpy(data, &type, 2);
|
||||
|
@ -195,9 +195,7 @@ SMFSource::flush_header ()
|
|||
assert(_fd);
|
||||
fseek(_fd, 0, 0);
|
||||
write_chunk("MThd", 6, data);
|
||||
//if (_track_size > 0) {
|
||||
write_chunk_header("MTrk", _track_size);
|
||||
//}
|
||||
write_chunk_header("MTrk", _track_size);
|
||||
|
||||
fflush(_fd);
|
||||
|
||||
|
@ -446,7 +444,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
|
|||
void
|
||||
SMFSource::append_event_unlocked(const MidiEvent& ev)
|
||||
{
|
||||
/*printf("SMF - writing event, time = %lf, size = %u, data = ", ev.time(), ev.size());
|
||||
/*printf("SMF %s - writing event, time = %lf, size = %u, data = ", _path.c_str(), ev.time(), ev.size());
|
||||
for (size_t i=0; i < ev.size(); ++i) {
|
||||
printf("%X ", ev.buffer()[i]);
|
||||
}
|
||||
|
@ -636,6 +634,12 @@ SMFSource::move_to_trash (const string trash_dir_name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
SMFSource::safe_file_extension(const Glib::ustring& file)
|
||||
{
|
||||
return (file.rfind(".mid") != Glib::ustring::npos);
|
||||
}
|
||||
|
||||
// FIXME: Merge this with audiofilesource somehow (make a generic filesource?)
|
||||
bool
|
||||
SMFSource::find (string pathstr, bool must_exist, bool& isnew)
|
||||
|
|
Loading…
Reference in New Issue