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:
David Robillard 2008-02-18 19:45:52 +00:00
parent 1b2fe7bf34
commit fbfb26b45c
21 changed files with 704 additions and 115 deletions

View File

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

View File

@ -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 */

View File

@ -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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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__ */

View File

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

View File

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

View File

@ -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 */

View File

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

View File

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

View File

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

View File

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

294
libs/ardour/smf_reader.cc Normal file
View File

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

View File

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