mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-10 08:36:32 +01:00
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
21 changed files with 704 additions and 115 deletions
|
|
@ -156,10 +156,11 @@ style "default_button"
|
||||||
font_name = "%FONT_SMALL%"
|
font_name = "%FONT_SMALL%"
|
||||||
fg[ACTIVE] = { 1.0, 1.0, 1.0 }
|
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[ACTIVE] = "#565690"
|
||||||
bg[PRELIGHT] = { 0.41, 0.45, 0.49 }
|
bg[PRELIGHT] = { 0.49, 0.53, 0.59 }
|
||||||
bg[INSENSITIVE] = { 0.11, 0.15, 0.19 }
|
bg[INSENSITIVE] = { 0.31, 0.35, 0.36 }
|
||||||
|
fg[INSENSITIVE] = { 0.61, 0.65, 0.67 }
|
||||||
bg[SELECTED] = { 0.11, 0.15, 0.19 }
|
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 (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 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 */
|
/* Nudge track */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@
|
||||||
#include <ardour/stretch.h>
|
#include <ardour/stretch.h>
|
||||||
#include <ardour/location.h>
|
#include <ardour/location.h>
|
||||||
#include <ardour/audioregion.h>
|
#include <ardour/audioregion.h>
|
||||||
|
#include <ardour/track.h>
|
||||||
|
|
||||||
#include "audio_clock.h"
|
#include "audio_clock.h"
|
||||||
#include "gtk-custom-ruler.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&);
|
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 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,
|
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 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 target_regions, int target_tracks, boost::shared_ptr<ARDOUR::Track>&, 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,
|
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::AudioTrack>& existing_track);
|
boost::shared_ptr<ARDOUR::Track>& existing_track);
|
||||||
|
|
||||||
boost::shared_ptr<ARDOUR::AudioTrack> get_nth_selected_audio_track (int nth) const;
|
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 */
|
/* 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 */
|
/* 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::session_sensitive_actions.push_back (act);
|
||||||
|
|
||||||
ActionManager::register_toggle_action (editor_actions, X_("ToggleWaveformVisibility"), _("Show Waveforms"), mem_fun (*this, &Editor::toggle_waveform_visibility));
|
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/audioplaylist.h>
|
||||||
#include <ardour/audioregion.h>
|
#include <ardour/audioregion.h>
|
||||||
#include <ardour/audio_diskstream.h>
|
#include <ardour/audio_diskstream.h>
|
||||||
|
#include <ardour/midi_track.h>
|
||||||
|
#include <ardour/midi_region.h>
|
||||||
#include <ardour/utils.h>
|
#include <ardour/utils.h>
|
||||||
#include <ardour/audio_track.h>
|
#include <ardour/audio_track.h>
|
||||||
#include <ardour/audioplaylist.h>
|
#include <ardour/audioplaylist.h>
|
||||||
|
|
@ -51,6 +53,7 @@
|
||||||
#include "sfdb_ui.h"
|
#include "sfdb_ui.h"
|
||||||
#include "editing.h"
|
#include "editing.h"
|
||||||
#include "audio_time_axis.h"
|
#include "audio_time_axis.h"
|
||||||
|
#include "midi_time_axis.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
@ -70,13 +73,13 @@ void
|
||||||
Editor::add_external_audio_action (ImportMode mode_hint)
|
Editor::add_external_audio_action (ImportMode mode_hint)
|
||||||
{
|
{
|
||||||
if (session == 0) {
|
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 ();
|
msg.run ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sfbrowser == 0) {
|
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 {
|
} else {
|
||||||
sfbrowser->set_mode (mode_hint);
|
sfbrowser->set_mode (mode_hint);
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +94,7 @@ Editor::external_audio_dialog ()
|
||||||
uint32_t track_cnt;
|
uint32_t track_cnt;
|
||||||
|
|
||||||
if (session == 0) {
|
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 ();
|
msg.run ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +112,7 @@ Editor::external_audio_dialog ()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sfbrowser == 0) {
|
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 {
|
} else {
|
||||||
sfbrowser->reset (track_cnt);
|
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;
|
bool wave_name_exists = false;
|
||||||
|
|
||||||
for (SourceMap::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
|
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 (i->second->path()));
|
||||||
|
|
||||||
string tmp (Glib::path_get_basename (afs->path()));
|
|
||||||
|
|
||||||
if (tmp == wave_name) {
|
if (tmp == wave_name) {
|
||||||
wave_name_exists = true;
|
wave_name_exists = true;
|
||||||
|
|
@ -281,10 +282,40 @@ Editor::get_nth_selected_audio_track (int nth) const
|
||||||
return atv->audio_track();
|
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
|
void
|
||||||
Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mode, SrcQuality quality, nframes64_t& pos)
|
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;
|
vector<ustring> to_import;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
int nth = 0;
|
int nth = 0;
|
||||||
|
|
@ -409,7 +440,7 @@ Editor::do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode
|
||||||
void
|
void
|
||||||
Editor::_do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos)
|
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 check_sample_rate = true;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
vector<ustring> to_embed;
|
vector<ustring> to_embed;
|
||||||
|
|
@ -474,7 +505,7 @@ Editor::_do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mod
|
||||||
|
|
||||||
int
|
int
|
||||||
Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality quality, nframes64_t& pos,
|
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());
|
WindowTitle title = string_compose (_("importing %1"), paths.front());
|
||||||
|
|
||||||
|
|
@ -535,7 +566,7 @@ Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality qual
|
||||||
int
|
int
|
||||||
Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
|
Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
|
||||||
bool& check_sample_rate, ImportMode mode, nframes64_t& pos, int target_regions, int target_tracks,
|
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;
|
boost::shared_ptr<AudioFileSource> source;
|
||||||
SourceList sources;
|
SourceList sources;
|
||||||
|
|
@ -688,9 +719,9 @@ Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
|
||||||
|
|
||||||
int
|
int
|
||||||
Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64_t& pos, ImportMode mode,
|
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;
|
ustring region_name;
|
||||||
uint32_t input_chan = 0;
|
uint32_t input_chan = 0;
|
||||||
uint32_t output_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) {
|
if (target_regions == 1) {
|
||||||
|
|
||||||
/* take all the sources we have and package them up as a region */
|
/* 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);
|
region_name = region_name_from_path (paths.front(), (sources.size() > 1), false);
|
||||||
|
|
||||||
regions.push_back (boost::dynamic_pointer_cast<AudioRegion>
|
regions.push_back (RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0,
|
||||||
(RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0,
|
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)));
|
||||||
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External))));
|
|
||||||
|
|
||||||
} else if (target_regions == -1) {
|
} else if (target_regions == -1) {
|
||||||
|
|
||||||
|
|
@ -726,13 +758,10 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
|
||||||
just_one.clear ();
|
just_one.clear ();
|
||||||
just_one.push_back (*x);
|
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 (RegionFactory::create (just_one, 0, (*x)->length(), region_name, 0,
|
||||||
|
Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)));
|
||||||
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))));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -755,9 +784,9 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
|
||||||
|
|
||||||
int n = 0;
|
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) {
|
if (target_tracks != 1) {
|
||||||
track.reset ();
|
track.reset ();
|
||||||
|
|
@ -776,9 +805,12 @@ Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_t in_chans, uint32_t out_chans, nframes64_t& pos,
|
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<AudioTrack>& existing_track)
|
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) {
|
switch (mode) {
|
||||||
case ImportAsRegion:
|
case ImportAsRegion:
|
||||||
/* relax, its been done */
|
/* relax, its been done */
|
||||||
|
|
@ -788,7 +820,11 @@ Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_
|
||||||
{
|
{
|
||||||
if (!existing_track) {
|
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) {
|
if (!existing_track) {
|
||||||
return -1;
|
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<Playlist> playlist = existing_track->diskstream()->playlist();
|
||||||
boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
|
boost::shared_ptr<Region> copy (RegionFactory::create (region));
|
||||||
begin_reversible_command (_("insert sndfile"));
|
begin_reversible_command (_("insert file"));
|
||||||
XMLNode &before = playlist->get_state();
|
XMLNode &before = playlist->get_state();
|
||||||
playlist->add_region (copy, pos);
|
playlist->add_region (copy, pos);
|
||||||
session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state()));
|
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:
|
case ImportAsTrack:
|
||||||
{
|
{
|
||||||
if (!existing_track) {
|
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()) {
|
if (at.empty()) {
|
||||||
return -1;
|
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());
|
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);
|
existing_track->diskstream()->playlist()->add_region (copy, pos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -826,9 +873,12 @@ Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_
|
||||||
|
|
||||||
case ImportAsTapeTrack:
|
case ImportAsTapeTrack:
|
||||||
{
|
{
|
||||||
|
if (!ar)
|
||||||
|
return -1;
|
||||||
|
|
||||||
list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Destructive));
|
list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Destructive));
|
||||||
if (!at.empty()) {
|
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()->set_name (basename_nosuffix (copy->name()));
|
||||||
at.front()->diskstream()->playlist()->add_region (copy, pos);
|
at.front()->diskstream()->playlist()->add_region (copy, pos);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Canvas {
|
||||||
|
|
||||||
LinesetClass Lineset::lineset_class;
|
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)
|
Lineset::Line::Line(double c, double w, uint32_t color)
|
||||||
: coord(c)
|
: coord(c)
|
||||||
|
|
@ -142,7 +142,7 @@ Lineset::change_line_width(double coord, double width) {
|
||||||
|
|
||||||
if(it != lines.end()) {
|
if(it != lines.end()) {
|
||||||
if(l.coord + width > it->coord) {
|
if(l.coord + width > it->coord) {
|
||||||
cerr << overlap_error_str << endl;
|
//cerr << overlap_error_str << endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -171,14 +171,14 @@ Lineset::add_line(double coord, double width, uint32_t color) {
|
||||||
/* overlap checking */
|
/* overlap checking */
|
||||||
if(it != lines.end()) {
|
if(it != lines.end()) {
|
||||||
if(l.coord + l.width > it->coord) {
|
if(l.coord + l.width > it->coord) {
|
||||||
cerr << overlap_error_str << endl;
|
//cerr << overlap_error_str << endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(it != lines.begin()) {
|
if(it != lines.begin()) {
|
||||||
--it;
|
--it;
|
||||||
if(l.coord < it->coord + it->width) {
|
if(l.coord < it->coord + it->width) {
|
||||||
cerr << overlap_error_str << endl;
|
//cerr << overlap_error_str << endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@
|
||||||
#include <ardour/auditioner.h>
|
#include <ardour/auditioner.h>
|
||||||
#include <ardour/audioregion.h>
|
#include <ardour/audioregion.h>
|
||||||
#include <ardour/audiofilesource.h>
|
#include <ardour/audiofilesource.h>
|
||||||
|
#include <ardour/smf_source.h>
|
||||||
|
#include <ardour/smf_reader.h>
|
||||||
#include <ardour/region_factory.h>
|
#include <ardour/region_factory.h>
|
||||||
#include <ardour/source_factory.h>
|
#include <ardour/source_factory.h>
|
||||||
#include <ardour/session.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.get_selection()->set_mode (SELECTION_MULTIPLE);
|
||||||
found_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::found_list_view_activated));
|
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));
|
audio_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_audio_filter));
|
||||||
custom_filter.set_name (_("Audio files"));
|
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.add_pattern ("*.*");
|
||||||
matchall_filter.set_name (_("All files"));
|
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.add_filter (matchall_filter);
|
||||||
chooser.set_select_multiple (true);
|
chooser.set_select_multiple (true);
|
||||||
chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
|
chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview));
|
||||||
|
|
@ -552,11 +558,17 @@ SoundFileBrowser::meter ()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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);
|
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
|
void
|
||||||
SoundFileBrowser::update_preview ()
|
SoundFileBrowser::update_preview ()
|
||||||
{
|
{
|
||||||
|
|
@ -894,6 +906,14 @@ SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool&
|
||||||
src_needed = true;
|
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 {
|
} else {
|
||||||
err = true;
|
err = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,8 @@ class SoundFileBrowser : public ArdourDialog
|
||||||
protected:
|
protected:
|
||||||
bool resetting_ourselves;
|
bool resetting_ourselves;
|
||||||
|
|
||||||
Gtk::FileFilter custom_filter;
|
Gtk::FileFilter audio_filter;
|
||||||
|
Gtk::FileFilter midi_filter;
|
||||||
Gtk::FileFilter matchall_filter;
|
Gtk::FileFilter matchall_filter;
|
||||||
SoundFileBox preview;
|
SoundFileBox preview;
|
||||||
Gtk::HBox hpacker;
|
Gtk::HBox hpacker;
|
||||||
|
|
@ -159,7 +160,8 @@ class SoundFileBrowser : public ArdourDialog
|
||||||
|
|
||||||
void chooser_file_activated ();
|
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; }
|
virtual bool reset_options() { return true; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ session_time.cc
|
||||||
session_transport.cc
|
session_transport.cc
|
||||||
session_utils.cc
|
session_utils.cc
|
||||||
silentfilesource.cc
|
silentfilesource.cc
|
||||||
|
smf_reader.cc
|
||||||
smf_source.cc
|
smf_source.cc
|
||||||
sndfile_helpers.cc
|
sndfile_helpers.cc
|
||||||
sndfilesource.cc
|
sndfilesource.cc
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ struct MidiEvent {
|
||||||
if (_owns_buffer) {
|
if (_owns_buffer) {
|
||||||
if (copy._buffer) {
|
if (copy._buffer) {
|
||||||
if (!_buffer || _size < copy._size)
|
if (!_buffer || _size < copy._size)
|
||||||
_buffer = (Byte*)realloc(_buffer, copy._size);
|
_buffer = (Byte*)::realloc(_buffer, copy._size);
|
||||||
memcpy(_buffer, copy._buffer, copy._size);
|
memcpy(_buffer, copy._buffer, copy._size);
|
||||||
} else {
|
} else {
|
||||||
free(_buffer);
|
free(_buffer);
|
||||||
|
|
@ -130,6 +130,11 @@ struct MidiEvent {
|
||||||
_owns_buffer = own;
|
_owns_buffer = own;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void realloc(size_t size) {
|
||||||
|
assert(_owns_buffer);
|
||||||
|
_buffer = (Byte*) ::realloc(_buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
inline void set_buffer(Byte* buf) { _buffer = buf; }
|
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,
|
/** Return the size of the given event NOT including the status byte,
|
||||||
* or -1 if unknown (eg sysex)
|
* or -1 if unknown (eg sysex)
|
||||||
*/
|
*/
|
||||||
int
|
static inline int
|
||||||
midi_event_size(unsigned char status)
|
midi_event_size(unsigned char status)
|
||||||
{
|
{
|
||||||
if (status >= 0x80 && status <= 0xE0) {
|
if (status >= 0x80 && status <= 0xE0) {
|
||||||
|
|
|
||||||
89
libs/ardour/ardour/smf_reader.h
Normal file
89
libs/ardour/ardour/smf_reader.h
Normal 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__ */
|
||||||
|
|
||||||
|
|
@ -63,8 +63,10 @@ class SMFSource : public MidiSource {
|
||||||
|
|
||||||
bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); }
|
bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); }
|
||||||
int set_source_name (string newname, bool destructive);
|
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 set_allow_remove_if_empty (bool yn);
|
||||||
void mark_for_remove();
|
void mark_for_remove();
|
||||||
|
|
@ -120,7 +122,7 @@ class SMFSource : public MidiSource {
|
||||||
static const uint16_t _ppqn = 19200;
|
static const uint16_t _ppqn = 19200;
|
||||||
|
|
||||||
uint16_t _channel;
|
uint16_t _channel;
|
||||||
string _path;
|
Glib::ustring _path;
|
||||||
Flag _flags;
|
Flag _flags;
|
||||||
string _take_id;
|
string _take_id;
|
||||||
bool _allow_remove_if_empty;
|
bool _allow_remove_if_empty;
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,9 @@ class Source : public SessionObject, public ARDOUR::Readable
|
||||||
time_t timestamp() const { return _timestamp; }
|
time_t timestamp() const { return _timestamp; }
|
||||||
void stamp (time_t when) { _timestamp = when; }
|
void stamp (time_t when) { _timestamp = when; }
|
||||||
|
|
||||||
/** @return the number of items in this source */
|
|
||||||
nframes_t length() const { return _length; }
|
nframes_t length() const { return _length; }
|
||||||
|
|
||||||
|
virtual Glib::ustring path() const = 0;
|
||||||
|
|
||||||
virtual nframes_t natural_position() const { return 0; }
|
virtual nframes_t natural_position() const { return 0; }
|
||||||
|
|
||||||
|
|
@ -88,10 +89,10 @@ class Source : public SessionObject, public ARDOUR::Readable
|
||||||
AnalysisFeatureList transients;
|
AnalysisFeatureList transients;
|
||||||
std::string get_transients_path() const;
|
std::string get_transients_path() const;
|
||||||
int load_transients (const std::string&);
|
int load_transients (const std::string&);
|
||||||
|
|
||||||
|
void update_length (nframes_t pos, nframes_t cnt);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void update_length (nframes_t pos, nframes_t cnt);
|
|
||||||
|
|
||||||
DataType _type;
|
DataType _type;
|
||||||
time_t _timestamp;
|
time_t _timestamp;
|
||||||
nframes_t _length;
|
nframes_t _length;
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@
|
||||||
|
|
||||||
#include <ardour/ardour.h>
|
#include <ardour/ardour.h>
|
||||||
#include <ardour/session.h>
|
#include <ardour/session.h>
|
||||||
|
#include <ardour/session_directory.h>
|
||||||
#include <ardour/audio_diskstream.h>
|
#include <ardour/audio_diskstream.h>
|
||||||
|
#include <ardour/audioengine.h>
|
||||||
#include <ardour/sndfilesource.h>
|
#include <ardour/sndfilesource.h>
|
||||||
#include <ardour/sndfile_helpers.h>
|
#include <ardour/sndfile_helpers.h>
|
||||||
#include <ardour/audioregion.h>
|
#include <ardour/audioregion.h>
|
||||||
|
|
@ -47,6 +49,9 @@
|
||||||
#include <ardour/source_factory.h>
|
#include <ardour/source_factory.h>
|
||||||
#include <ardour/resampled_source.h>
|
#include <ardour/resampled_source.h>
|
||||||
#include <ardour/analyser.h>
|
#include <ardour/analyser.h>
|
||||||
|
#include <ardour/smf_reader.h>
|
||||||
|
#include <ardour/smf_source.h>
|
||||||
|
#include <ardour/tempo.h>
|
||||||
|
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
||||||
|
|
@ -66,23 +71,25 @@ open_importable_source (const string& path, nframes_t samplerate, ARDOUR::SrcQua
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string
|
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];
|
char buf[PATH_MAX+1];
|
||||||
bool goodfile = false;
|
bool goodfile = false;
|
||||||
string base(basename);
|
string base(basename);
|
||||||
|
const char* ext = (type == DataType::AUDIO) ? "wav" : "mid";
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (channels == 2) {
|
|
||||||
|
if (type == DataType::AUDIO && channels == 2) {
|
||||||
if (channel == 0) {
|
if (channel == 0) {
|
||||||
snprintf (buf, sizeof(buf), "%s-L.wav", base.c_str());
|
snprintf (buf, sizeof(buf), "%s-L.wav", base.c_str());
|
||||||
} else {
|
} else {
|
||||||
snprintf (buf, sizeof(buf), "%s-R.wav", base.c_str());
|
snprintf (buf, sizeof(buf), "%s-R.wav", base.c_str());
|
||||||
}
|
}
|
||||||
} else if (channels > 1) {
|
} 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 {
|
} 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;
|
vector<string> new_paths;
|
||||||
const string basename = basename_nosuffix (import_file_path);
|
const string basename = basename_nosuffix (import_file_path);
|
||||||
|
|
||||||
|
SessionDirectory sdir(session_dir);
|
||||||
|
|
||||||
for (uint n = 0; n < channels; ++n) {
|
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 += '/';
|
||||||
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);
|
new_paths.push_back (filepath);
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +142,7 @@ get_paths_for_new_sources (const bool allow_replacing, const string& import_file
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
map_existing_mono_sources (const vector<string>& new_paths, Session& sess,
|
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();
|
for (vector<string>::const_iterator i = new_paths.begin();
|
||||||
i != new_paths.end(); ++i)
|
i != new_paths.end(); ++i)
|
||||||
|
|
@ -140,14 +154,14 @@ map_existing_mono_sources (const vector<string>& new_paths, Session& sess,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
newfiles.push_back(boost::dynamic_pointer_cast<AudioFileSource>(source));
|
newfiles.push_back(boost::dynamic_pointer_cast<Source>(source));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
|
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();
|
for (vector<string>::const_iterator i = new_paths.begin();
|
||||||
i != new_paths.end(); ++i)
|
i != new_paths.end(); ++i)
|
||||||
|
|
@ -156,13 +170,16 @@ create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
const DataType type = ((*i).rfind(".mid") != string::npos)
|
||||||
|
? DataType::MIDI : DataType::AUDIO;
|
||||||
|
|
||||||
source = SourceFactory::createWritable (
|
source = SourceFactory::createWritable (
|
||||||
DataType::AUDIO,
|
type,
|
||||||
sess,
|
sess,
|
||||||
i->c_str(),
|
i->c_str(),
|
||||||
false, // destructive
|
false, // destructive
|
||||||
samplerate
|
samplerate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (const failed_constructor& err)
|
catch (const failed_constructor& err)
|
||||||
{
|
{
|
||||||
|
|
@ -170,7 +187,7 @@ create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
newfiles.push_back(boost::dynamic_pointer_cast<AudioFileSource>(source));
|
newfiles.push_back(boost::dynamic_pointer_cast<Source>(source));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -197,9 +214,10 @@ compose_status_message (const string& path,
|
||||||
|
|
||||||
static void
|
static void
|
||||||
write_audio_data_to_new_files (ImportableSource* source, Session::import_status& status,
|
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;
|
const nframes_t nframes = ResampledImportableSource::blocksize;
|
||||||
|
boost::shared_ptr<AudioFileSource> afs;
|
||||||
uint channels = source->channels();
|
uint channels = source->channels();
|
||||||
|
|
||||||
boost::scoped_array<float> data(new float[nframes * 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 */
|
/* flush to disk */
|
||||||
|
|
||||||
for (chn = 0; chn < channels; ++chn) {
|
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;
|
read_count += nread;
|
||||||
|
|
@ -245,9 +265,71 @@ write_audio_data_to_new_files (ImportableSource* source, Session::import_status&
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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
|
// This function is still unable to cleanly update an existing source, even though
|
||||||
|
|
@ -258,8 +340,10 @@ void
|
||||||
Session::import_audiofiles (import_status& status)
|
Session::import_audiofiles (import_status& status)
|
||||||
{
|
{
|
||||||
uint32_t cnt = 1;
|
uint32_t cnt = 1;
|
||||||
typedef vector<boost::shared_ptr<AudioFileSource> > AudioSources;
|
typedef vector<boost::shared_ptr<Source> > Sources;
|
||||||
AudioSources all_new_sources;
|
Sources all_new_sources;
|
||||||
|
boost::shared_ptr<AudioFileSource> afs;
|
||||||
|
uint channels = 0;
|
||||||
|
|
||||||
status.sources.clear ();
|
status.sources.clear ();
|
||||||
|
|
||||||
|
|
@ -268,24 +352,44 @@ Session::import_audiofiles (import_status& status)
|
||||||
++p, ++cnt)
|
++p, ++cnt)
|
||||||
{
|
{
|
||||||
std::auto_ptr<ImportableSource> source;
|
std::auto_ptr<ImportableSource> source;
|
||||||
|
std::auto_ptr<SMFReader> smf_reader;
|
||||||
|
|
||||||
|
const DataType type = ((*p).rfind(".mid") != string::npos)
|
||||||
|
? DataType::MIDI : DataType::AUDIO;
|
||||||
|
|
||||||
try
|
if (type == DataType::AUDIO) {
|
||||||
{
|
try
|
||||||
source = open_importable_source (*p, frame_rate(), status.quality);
|
{
|
||||||
}
|
source = open_importable_source (*p, frame_rate(), status.quality);
|
||||||
catch (const failed_constructor& err)
|
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;
|
error << string_compose(_("Import: cannot open input sound file \"%1\""), (*p)) << endmsg;
|
||||||
return;
|
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,
|
vector<string> new_paths = get_paths_for_new_sources (status.replace_existing_source,
|
||||||
*p,
|
*p,
|
||||||
get_best_session_directory_for_new_source (),
|
get_best_session_directory_for_new_source (),
|
||||||
source->channels());
|
channels);
|
||||||
|
|
||||||
AudioSources newfiles;
|
Sources newfiles;
|
||||||
|
|
||||||
if (status.replace_existing_source) {
|
if (status.replace_existing_source) {
|
||||||
fatal << "THIS IS NOT IMPLEMENTED YET, IT SHOULD NEVER GET CALLED!!! DYING!" << endl;
|
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;
|
if (status.cancel) break;
|
||||||
|
|
||||||
for (AudioSources::iterator i = newfiles.begin(); i != newfiles.end(); ++i) {
|
for (Sources::iterator i = newfiles.begin(); i != newfiles.end(); ++i) {
|
||||||
(*i)->prepare_for_peakfile_writes ();
|
if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(*i)) != 0) {
|
||||||
|
afs->prepare_for_peakfile_writes ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status.doing_what = compose_status_message (*p, source->samplerate(),
|
if (source.get()) { // audio
|
||||||
frame_rate(), cnt, status.paths.size());
|
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);
|
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) {
|
if (!status.cancel) {
|
||||||
|
|
@ -318,10 +428,14 @@ Session::import_audiofiles (import_status& status)
|
||||||
|
|
||||||
/* flush the final length(s) to the header(s) */
|
/* 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);
|
cout << "NEW SOURCE: " << (*x)->path() << endl;
|
||||||
(*x)->done_with_peakfile_writes ();
|
|
||||||
|
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 */
|
/* now that there is data there, requeue the file for analysis */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -670,7 +670,7 @@ MidiDiskstream::set_pending_overwrite (bool yn)
|
||||||
int
|
int
|
||||||
MidiDiskstream::overwrite_existing_buffers ()
|
MidiDiskstream::overwrite_existing_buffers ()
|
||||||
{
|
{
|
||||||
cerr << "MDS: overwrite_existing_buffers() (does nothing)" << endl;
|
//cerr << "MDS: overwrite_existing_buffers() (does nothing)" << endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -433,7 +433,7 @@ MidiModel::append_note_off_unlocked(double time, uint8_t note_num)
|
||||||
Note& note = *_notes[*n].get();
|
Note& note = *_notes[*n].get();
|
||||||
//cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl;
|
//cerr << (unsigned)(uint8_t)note.note() << " ? " << (unsigned)note_num << endl;
|
||||||
if (note.note() == note_num) {
|
if (note.note() == note_num) {
|
||||||
assert(time > note.time());
|
assert(time >= note.time());
|
||||||
note.set_duration(time - note.time());
|
note.set_duration(time - note.time());
|
||||||
_write_notes.erase(n);
|
_write_notes.erase(n);
|
||||||
//cerr << "MM resolved note, duration: " << note.duration() << endl;
|
//cerr << "MM resolved note, duration: " << note.duration() << endl;
|
||||||
|
|
|
||||||
|
|
@ -522,6 +522,8 @@ Playlist::set_region_ownership ()
|
||||||
void
|
void
|
||||||
Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
|
Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
|
||||||
{
|
{
|
||||||
|
assert(region->data_type() == _type);
|
||||||
|
|
||||||
RegionSortByPosition cmp;
|
RegionSortByPosition cmp;
|
||||||
nframes_t old_length = 0;
|
nframes_t old_length = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,8 @@ RegionFactory::create (Session& session, XMLNode& node, bool yn)
|
||||||
boost::shared_ptr<Region>
|
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)
|
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()) {
|
if (srcs.empty()) {
|
||||||
return boost::shared_ptr<Region>();
|
return boost::shared_ptr<Region>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
294
libs/ardour/smf_reader.cc
Normal file
294
libs/ardour/smf_reader.cc
Normal 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
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ SMFSource::SMFSource (Session& s, std::string path, Flag flags)
|
||||||
throw failed_constructor ();
|
throw failed_constructor ();
|
||||||
}
|
}
|
||||||
|
|
||||||
//cerr << "SMF Source path: " << path << endl;
|
cerr << "SMF Source path: " << path << endl;
|
||||||
|
|
||||||
assert(_name.find("/") == string::npos);
|
assert(_name.find("/") == string::npos);
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +99,7 @@ SMFSource::SMFSource (Session& s, const XMLNode& node)
|
||||||
throw failed_constructor ();
|
throw failed_constructor ();
|
||||||
}
|
}
|
||||||
|
|
||||||
//cerr << "SMF Source name: " << _name << endl;
|
cerr << "SMF Source name: " << _name << endl;
|
||||||
|
|
||||||
assert(_name.find("/") == string::npos);
|
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 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 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];
|
char data[6];
|
||||||
memcpy(data, &type, 2);
|
memcpy(data, &type, 2);
|
||||||
|
|
@ -195,9 +195,7 @@ SMFSource::flush_header ()
|
||||||
assert(_fd);
|
assert(_fd);
|
||||||
fseek(_fd, 0, 0);
|
fseek(_fd, 0, 0);
|
||||||
write_chunk("MThd", 6, data);
|
write_chunk("MThd", 6, data);
|
||||||
//if (_track_size > 0) {
|
write_chunk_header("MTrk", _track_size);
|
||||||
write_chunk_header("MTrk", _track_size);
|
|
||||||
//}
|
|
||||||
|
|
||||||
fflush(_fd);
|
fflush(_fd);
|
||||||
|
|
||||||
|
|
@ -446,7 +444,7 @@ SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
|
||||||
void
|
void
|
||||||
SMFSource::append_event_unlocked(const MidiEvent& ev)
|
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) {
|
for (size_t i=0; i < ev.size(); ++i) {
|
||||||
printf("%X ", ev.buffer()[i]);
|
printf("%X ", ev.buffer()[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -636,6 +634,12 @@ SMFSource::move_to_trash (const string trash_dir_name)
|
||||||
return 0;
|
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?)
|
// FIXME: Merge this with audiofilesource somehow (make a generic filesource?)
|
||||||
bool
|
bool
|
||||||
SMFSource::find (string pathstr, bool must_exist, bool& isnew)
|
SMFSource::find (string pathstr, bool must_exist, bool& isnew)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue