--- gigedit/trunk/src/mainwindow.cpp 2007/03/04 22:00:46 1069 +++ gigedit/trunk/src/mainwindow.cpp 2007/03/17 17:46:09 1101 @@ -20,497 +20,33 @@ #include #include -#include "mainwindow.h" - #include +#include #include +#include + #if GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6 #define ABOUT_DIALOG #include #endif -#define _(String) gettext(String) - -bool update_gui; - -uint8_t& access_UnityNote(gig::DimensionRegion* dimreg) -{ - return dimreg->UnityNote; -} -int16_t& access_FineTune(gig::DimensionRegion* dimreg) -{ - return dimreg->FineTune; -} -uint32_t& access_SampleLoops(gig::DimensionRegion* dimreg) -{ - return dimreg->SampleLoops; -} -uint8_t& access_Crossfade_in_start(gig::DimensionRegion* dimreg) -{ - return dimreg->Crossfade.in_start; -} -uint8_t& access_Crossfade_in_end(gig::DimensionRegion* dimreg) -{ - return dimreg->Crossfade.in_end; -} -uint8_t& access_Crossfade_out_start(gig::DimensionRegion* dimreg) -{ - return dimreg->Crossfade.out_start; -} -uint8_t& access_Crossfade_out_end(gig::DimensionRegion* dimreg) -{ - return dimreg->Crossfade.out_end; -} - -namespace { - const char* const controlChangeTexts[] = { - "none", "channelaftertouch", "velocity", - 0, - "modwheel", // "Modulation Wheel or Lever", - "breath", // "Breath Controller", - 0, - "foot", // "Foot Controller", - "portamentotime", // "Portamento Time", - 0, 0, 0, 0, 0, 0, - "effect1", // "Effect Control 1", - "effect2", // "Effect Control 2", - 0, 0, - "genpurpose1", // "General Purpose Controller 1", - "genpurpose2", // "General Purpose Controller 2", - "genpurpose3", // "General Purpose Controller 3", - "genpurpose4", // "General Purpose Controller 4", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - "sustainpedal", // "Damper Pedal on/off (Sustain)", - "portamento", // "Portamento On/Off", - "sostenuto", // "Sustenuto On/Off", - "softpedal", // "Soft Pedal On/Off", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - "genpurpose5", // "General Purpose Controller 5", - "genpurpose6", // "General Purpose Controller 6", - "genpurpose7", // "General Purpose Controller 7", - "genpurpose8", // "General Purpose Controller 8", - 0, 0, 0, 0, 0, 0, 0, - "effect1depth", // "Effects 1 Depth", - "effect2depth", // "Effects 2 Depth", - "effect3depth", // "Effects 3 Depth", - "effect4depth", // "Effects 4 Depth", - "effect5depth", // "Effects 5 Depth" - }; -} - -void MainWindow::addString(char* labelText, Gtk::Label*& label, - Gtk::Entry*& widget) -{ - label = new Gtk::Label(Glib::ustring(labelText) + ":"); - label->set_alignment(Gtk::ALIGN_LEFT); - - table[pageno]->attach(*label, 1, 2, rowno, rowno + 1, - Gtk::FILL, Gtk::SHRINK); - - widget = new Gtk::Entry(); - - table[pageno]->attach(*widget, 2, 3, rowno, rowno + 1, - Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK); - - rowno++; -} - -LabelWidget::LabelWidget(char* labelText, Gtk::Widget& widget) : - label(Glib::ustring(labelText) + ":"), - widget(widget) -{ - label.set_alignment(Gtk::ALIGN_LEFT); -} - -void LabelWidget::set_sensitive(bool sensitive) -{ - label.set_sensitive(sensitive); - widget.set_sensitive(sensitive); -} - -NumEntryGain::NumEntryGain(char* labelText, - double lower = 0, double upper = 127, - int decimals = 0) : - NumEntry(labelText, lower, upper, decimals) -{ - spinbutton.signal_value_changed().connect( - sigc::mem_fun(*this, &NumEntryGain::value_changed)); -} - -void NumEntryGain::value_changed() -{ - if (dimreg && update_gui) { - dimreg->Gain = int32_t(spinbutton.get_value() * -655360.0); - } -} - -void NumEntryGain::set_dimreg(gig::DimensionRegion* dimreg) -{ - this->dimreg = 0; - set_value(dimreg->Gain / -655360.0); - this->dimreg = dimreg; -} - - -NumEntryPermille::NumEntryPermille(char* labelText, - uint16_t gig::DimensionRegion::* param, - double lower, double upper, int decimals) : - NumEntry(labelText, lower, upper, decimals), - param(param) -{ - spinbutton.signal_value_changed().connect( - sigc::mem_fun(*this, &NumEntryPermille::value_changed)); -} - -void NumEntryPermille::value_changed() -{ - if (dimreg && update_gui) { - dimreg->*param = uint16_t(spinbutton.get_value() * 10 + 0.5); - } -} - -void NumEntryPermille::set_dimreg(gig::DimensionRegion* dimreg) -{ - this->dimreg = 0; - set_value(dimreg->*param / 10.0); - this->dimreg = dimreg; -} - - -NoteEntry::NoteEntry(char* labelText, uint8_t& (*access)(gig::DimensionRegion*)) : - NumEntryX(labelText, access) -{ - spinbutton.signal_input().connect( - sigc::mem_fun(*this, &NoteEntry::on_input)); - spinbutton.signal_output().connect( - sigc::mem_fun(*this, &NoteEntry::on_output)); -} - -const char* notes[] = { - "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" -}; - - -// Convert the Entry text to a number -int NoteEntry::on_input(double* new_value) -{ - const char* str = spinbutton.get_text().c_str(); - - int i; - for (i = 11 ; i >= 0 ; i--) { - if (strncmp(str, notes[i], strlen(notes[i])) == 0) break; - } - if (i >= 0) { - char* endptr; - long x = strtol(str + strlen(notes[i]), &endptr, 10); - if (endptr != str + strlen(notes[i])) { - *new_value = i + (x + 1) * 12; - return true; - } - } - return Gtk::INPUT_ERROR; -} - -// Convert the Adjustment position to text -bool NoteEntry::on_output() -{ - int x = int(spinbutton.get_adjustment()->get_value()); - char buf[10]; - sprintf(buf, "%s%d", notes[x % 12], x / 12 - 1); - spinbutton.set_text(buf); - return true; -} - -BoolEntry::BoolEntry(char* labelText, bool gig::DimensionRegion::* param) : - LabelWidget(labelText, checkbutton), - param(param) -{ - checkbutton.signal_toggled().connect( - sigc::mem_fun(*this, &BoolEntry::value_changed)); -} - -void BoolEntry::value_changed() -{ - if (dimreg && update_gui) { - dimreg->*param = checkbutton.get_active(); - } -} - -void BoolEntry::set_dimreg(gig::DimensionRegion* dimreg) -{ - this->dimreg = 0; - checkbutton.set_active(dimreg->*param); - this->dimreg = dimreg; -} - -ChoiceEntryLeverageCtrl::ChoiceEntryLeverageCtrl( - char* labelText, - gig::leverage_ctrl_t gig::DimensionRegion::* param) : - align(0, 0, 0, 0), - LabelWidget(labelText, align), - param(param) -{ - for (int i = 0 ; i < 99 ; i++) { - if (controlChangeTexts[i]) { - combobox.append_text(controlChangeTexts[i]); - } - } - combobox.signal_changed().connect( - sigc::mem_fun(*this, &ChoiceEntryLeverageCtrl::value_changed)); - align.add(combobox); -} - -void ChoiceEntryLeverageCtrl::value_changed() -{ - if (dimreg && update_gui) { - int rowno = combobox.get_active_row_number(); - switch (rowno) - { - case -1: - break; - case 0: - (dimreg->*param).type = gig::leverage_ctrl_t::type_none; - break; - case 1: - (dimreg->*param).type = - gig::leverage_ctrl_t::type_channelaftertouch; - break; - case 2: - (dimreg->*param).type = gig::leverage_ctrl_t::type_velocity; - break; - default: - (dimreg->*param).type = gig::leverage_ctrl_t::type_controlchange; - int x = 3; - for (int cc = 0 ; cc < 96 ; cc++) { - if (controlChangeTexts[cc + 3]) { - if (rowno == x) { - (dimreg->*param).controller_number = cc; - break; - } - x++; - } - } - break; - } - } -} +#include +#include -void ChoiceEntryLeverageCtrl::set_dimreg(gig::DimensionRegion* dimreg) -{ - this->dimreg = 0; - gig::leverage_ctrl_t c = dimreg->*param; - int x; - switch (c.type) - { - case gig::leverage_ctrl_t::type_none: - x = 0; - break; - case gig::leverage_ctrl_t::type_channelaftertouch: - x = 1; - break; - case gig::leverage_ctrl_t::type_velocity: - x = 2; - break; - case gig::leverage_ctrl_t::type_controlchange: - x = -1; - for (int cc = 0 ; cc < 96 ; cc++) { - if (controlChangeTexts[cc + 3]) { - x++; - if (c.controller_number == cc) { - x += 3; - break; - } - } - } - break; - default: - x = -1; - break; - } - combobox.set_active(x); - this->dimreg = dimreg; -} +#include "mainwindow.h" -void MainWindow::addHeader(char* text) -{ - if (firstRowInBlock < rowno - 1) - { - Gtk::Label* filler = new Gtk::Label(" "); - table[pageno]->attach(*filler, 0, 1, firstRowInBlock, rowno, - Gtk::FILL, Gtk::SHRINK); - } - Glib::ustring str = ""; - str += text; - str += ""; - Gtk::Label* label = new Gtk::Label(str); - label->set_use_markup(); - label->set_alignment(Gtk::ALIGN_LEFT); - table[pageno]->attach(*label, 0, 3, rowno, rowno + 1, - Gtk::FILL, Gtk::SHRINK); - rowno++; - firstRowInBlock = rowno; -} +#define _(String) gettext(String) -void MainWindow::nextPage() -{ - if (firstRowInBlock < rowno - 1) - { - Gtk::Label* filler = new Gtk::Label(" "); - table[pageno]->attach(*filler, 0, 1, firstRowInBlock, rowno, - Gtk::FILL, Gtk::SHRINK); - } - pageno++; - rowno = 0; - firstRowInBlock = 0; -} - -void MainWindow::addProp(LabelWidget& prop) -{ - table[pageno]->attach(prop.label, 1, 2, rowno, rowno + 1, - Gtk::FILL, Gtk::SHRINK); - table[pageno]->attach(prop.widget, 2, 3, rowno, rowno + 1, - Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK); - rowno++; +template inline std::string ToString(T o) { + std::stringstream ss; + ss << o; + return ss.str(); } - - - -MainWindow::MainWindow() : -// eSample("Sample", wSample), - eVelocityUpperLimit("VelocityUpperLimit", - &gig::DimensionRegion::VelocityUpperLimit), - eEG1PreAttack("PreAttack", &gig::DimensionRegion::EG1PreAttack, 0, 100, 2), - eEG1Attack("Attack", &gig::DimensionRegion::EG1Attack, 0, 60, 3), - eEG1Decay1("Decay1", &gig::DimensionRegion::EG1Decay1, 0.005, 60, 3), - eEG1Decay2("Decay2", &gig::DimensionRegion::EG1Decay2, 0, 60, 3), - eEG1InfiniteSustain("InfiniteSustain", - &gig::DimensionRegion::EG1InfiniteSustain), - eEG1Sustain("Sustain", &gig::DimensionRegion::EG1Sustain, 0, 100, 2), - eEG1Release("Release", &gig::DimensionRegion::EG1Release, 0, 60, 3), - eEG1Hold("Hold", &gig::DimensionRegion::EG1Hold), - eEG1Controller("Controller", &gig::DimensionRegion::EG1Controller), - eEG1ControllerInvert("ControllerInvert", - &gig::DimensionRegion::EG1ControllerInvert), - eEG1ControllerAttackInfluence("ControllerAttackInfluence", - &gig::DimensionRegion::EG1ControllerAttackInfluence, - 0, 3), - eEG1ControllerDecayInfluence("ControllerDecayInfluence", - &gig::DimensionRegion::EG1ControllerDecayInfluence, - 0, 3), - eEG1ControllerReleaseInfluence("ControllerReleaseInfluence", - &gig::DimensionRegion::EG1ControllerReleaseInfluence, - 0, 3), - eLFO1Frequency("Frequency", &gig::DimensionRegion::LFO1Frequency, - 0.1, 10, 2), - eLFO1InternalDepth("InternalDepth", - &gig::DimensionRegion::LFO1InternalDepth, 0, 1200), - eLFO1ControlDepth("ControlDepth", &gig::DimensionRegion::LFO1ControlDepth, - 0, 1200), - eLFO1Controller("Controller", &gig::DimensionRegion::LFO1Controller), - eLFO1FlipPhase("FlipPhase", &gig::DimensionRegion::LFO1FlipPhase), - eLFO1Sync("Sync", &gig::DimensionRegion::LFO1Sync), - eEG2PreAttack("PreAttack", &gig::DimensionRegion::EG2PreAttack, 0, 100, 2), - eEG2Attack("Attack", &gig::DimensionRegion::EG2Attack, 0, 60, 3), - eEG2Decay1("Decay1", &gig::DimensionRegion::EG2Decay1, 0.005, 60, 3), - eEG2Decay2("Decay2", &gig::DimensionRegion::EG2Decay2, 0, 60, 3), - eEG2InfiniteSustain("InfiniteSustain", - &gig::DimensionRegion::EG2InfiniteSustain), - eEG2Sustain("Sustain", &gig::DimensionRegion::EG2Sustain, 0, 100, 2), - eEG2Release("Release", &gig::DimensionRegion::EG2Release, 0, 60, 3), - eEG2Controller("Controller", &gig::DimensionRegion::EG2Controller), - eEG2ControllerInvert("ControllerInvert", - &gig::DimensionRegion::EG2ControllerInvert), - eEG2ControllerAttackInfluence("ControllerAttackInfluence", - &gig::DimensionRegion::EG2ControllerAttackInfluence, - 0, 3), - eEG2ControllerDecayInfluence("ControllerDecayInfluence", - &gig::DimensionRegion::EG2ControllerDecayInfluence, - 0, 3), - eEG2ControllerReleaseInfluence("ControllerReleaseInfluence", - &gig::DimensionRegion::EG2ControllerReleaseInfluence, - 0, 3), - eLFO2Frequency("Frequency", &gig::DimensionRegion::LFO2Frequency, - 0.1, 10, 2), - eLFO2InternalDepth("InternalDepth", - &gig::DimensionRegion::LFO2InternalDepth, 0, 1200), - eLFO2ControlDepth("ControlDepth", - &gig::DimensionRegion::LFO2ControlDepth, 0, 1200), - eLFO2Controller("Controller", &gig::DimensionRegion::LFO2Controller), - eLFO2FlipPhase("FlipPhase", &gig::DimensionRegion::LFO2FlipPhase), - eLFO2Sync("Sync", &gig::DimensionRegion::LFO2Sync), - eEG3Attack("Attack", &gig::DimensionRegion::EG3Attack, 0, 10, 3), - eEG3Depth("Depth", &gig::DimensionRegion::EG3Depth, -1200, 1200), - eLFO3Frequency("Frequency", &gig::DimensionRegion::LFO3Frequency, - 0.1, 10, 2), - eLFO3InternalDepth("InternalDepth", - &gig::DimensionRegion::LFO3InternalDepth, 0, 1200), - eLFO3ControlDepth("ControlDepth", &gig::DimensionRegion::LFO3ControlDepth, - 0, 1200), - eLFO3Controller("Controller", &gig::DimensionRegion::LFO3Controller), - eLFO3Sync("Sync", &gig::DimensionRegion::LFO3Sync), - eVCFEnabled("Enabled", &gig::DimensionRegion::VCFEnabled), - eVCFType("Type", &gig::DimensionRegion::VCFType), - eVCFCutoffController("CutoffController", - &gig::DimensionRegion::VCFCutoffController), - eVCFCutoffControllerInvert("CutoffControllerInvert", - &gig::DimensionRegion::VCFCutoffControllerInvert), - eVCFCutoff("Cutoff", &gig::DimensionRegion::VCFCutoff), - eVCFVelocityCurve("VelocityCurve", &gig::DimensionRegion::VCFVelocityCurve), - eVCFVelocityScale("VelocityScale", &gig::DimensionRegion::VCFVelocityScale), - eVCFVelocityDynamicRange("VelocityDynamicRange", - &gig::DimensionRegion::VCFVelocityDynamicRange, - 0, 4), - eVCFResonance("Resonance", &gig::DimensionRegion::VCFResonance), - eVCFResonanceDynamic("ResonanceDynamic", - &gig::DimensionRegion::VCFResonanceDynamic), - eVCFResonanceController("ResonanceController", - &gig::DimensionRegion::VCFResonanceController), - eVCFKeyboardTracking("KeyboardTracking", - &gig::DimensionRegion::VCFKeyboardTracking), - eVCFKeyboardTrackingBreakpoint("KeyboardTrackingBreakpoint", - &gig::DimensionRegion::VCFKeyboardTrackingBreakpoint), - eVelocityResponseCurve("VelocityResponseCurve", - &gig::DimensionRegion::VelocityResponseCurve), - eVelocityResponseDepth("VelocityResponseDepth", - &gig::DimensionRegion::VelocityResponseDepth, 0, 4), - eVelocityResponseCurveScaling("VelocityResponseCurveScaling", - &gig::DimensionRegion::VelocityResponseCurveScaling), - eReleaseVelocityResponseCurve("ReleaseVelocityResponseCurve", - &gig::DimensionRegion::ReleaseVelocityResponseCurve), - eReleaseVelocityResponseDepth("ReleaseVelocityResponseDepth", - &gig::DimensionRegion::ReleaseVelocityResponseDepth, - 0, 4), - eReleaseTriggerDecay("ReleaseTriggerDecay", - &gig::DimensionRegion::ReleaseTriggerDecay, 0, 8), - eCrossfade_in_start("Crossfade.in_start", &access_Crossfade_in_start), - eCrossfade_in_end("Crossfade.in_end", &access_Crossfade_in_end), - eCrossfade_out_start("Crossfade.out_start", &access_Crossfade_out_start), - eCrossfade_out_end("Crossfade.out_end", &access_Crossfade_out_end), - ePitchTrack("PitchTrack", &gig::DimensionRegion::PitchTrack), - eDimensionBypass("DimensionBypass", &gig::DimensionRegion::DimensionBypass), - ePan("Pan", &gig::DimensionRegion::Pan, -64, 63), - eSelfMask("SelfMask", &gig::DimensionRegion::SelfMask), - eAttenuationController("AttenuationController", - &gig::DimensionRegion::AttenuationController), - eInvertAttenuationController("InvertAttenuationController", - &gig::DimensionRegion::InvertAttenuationController), - eAttenuationControllerThreshold("AttenuationControllerThreshold", - &gig::DimensionRegion::AttenuationControllerThreshold), - eChannelOffset("ChannelOffset", &gig::DimensionRegion::ChannelOffset, 0, 9), - eSustainDefeat("SustainDefeat", &gig::DimensionRegion::SustainDefeat), - eMSDecode("MSDecode", &gig::DimensionRegion::MSDecode), - eSampleStartOffset("SampleStartOffset", - &gig::DimensionRegion::SampleStartOffset, 0, 2000), - eUnityNote("UnityNote", &access_UnityNote), - eFineTune("FineTune", &access_FineTune, -49, 50), - eGain("Gain", -96, 0, 2), - eSampleLoops("SampleLoops", &access_SampleLoops, 0, 1) +MainWindow::MainWindow() { // set_border_width(5); - set_default_size(400, 200); +// set_default_size(400, 200); add(m_VBox); @@ -520,267 +56,28 @@ tree_sel_ref->signal_changed().connect( sigc::mem_fun(*this, &MainWindow::on_sel_change)); + // m_TreeView.set_reorderable(); + m_TreeView.signal_button_press_event().connect_notify( sigc::mem_fun(*this, &MainWindow::on_button_release)); - // Add the TreeView, inside a ScrolledWindow, with the button underneath: + // Add the TreeView tab, inside a ScrolledWindow, with the button underneath: m_ScrolledWindow.add(m_TreeView); - m_ScrolledWindow.set_size_request(400, 600); +// m_ScrolledWindow.set_size_request(200, 600); m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - for (int i = 0 ; i < 5 ; i++) { - table[i] = new Gtk::Table(3, 1); - table[i]->set_col_spacings(5); - } - - pageno = 0; - rowno = 0; - firstRowInBlock = 0; - - addString("Sample", lSample, wSample); - addProp(eVelocityUpperLimit); - addHeader("EG1"); - addProp(eEG1PreAttack); - addProp(eEG1Attack); - addProp(eEG1Decay1); - addProp(eEG1Decay2); - addProp(eEG1InfiniteSustain); - addProp(eEG1Sustain); - addProp(eEG1Release); - addProp(eEG1Hold); - addProp(eEG1Controller); - addProp(eEG1ControllerInvert); - addProp(eEG1ControllerAttackInfluence); - addProp(eEG1ControllerDecayInfluence); - addProp(eEG1ControllerReleaseInfluence); - addHeader("LFO1"); - addProp(eLFO1Frequency); - addProp(eLFO1InternalDepth); - addProp(eLFO1ControlDepth); - { - char* choices[] = { "internal", "modwheel", "breath", - "internal+modwheel", "internal+breath", 0 }; - static const gig::lfo1_ctrl_t values[] = { - gig::lfo1_ctrl_internal, - gig::lfo1_ctrl_modwheel, - gig::lfo1_ctrl_breath, - gig::lfo1_ctrl_internal_modwheel, - gig::lfo1_ctrl_internal_breath - }; - eLFO1Controller.set_choices(choices, values); - } - addProp(eLFO1Controller); - addProp(eLFO1FlipPhase); - addProp(eLFO1Sync); - - nextPage(); - addHeader("EG2"); - addProp(eEG2PreAttack); - addProp(eEG2Attack); - addProp(eEG2Decay1); - addProp(eEG2Decay2); - addProp(eEG2InfiniteSustain); - addProp(eEG2Sustain); - addProp(eEG2Release); - addProp(eEG2Controller); - addProp(eEG2ControllerInvert); - addProp(eEG2ControllerAttackInfluence); - addProp(eEG2ControllerDecayInfluence); - addProp(eEG2ControllerReleaseInfluence); - addHeader("LFO2"); - addProp(eLFO2Frequency); - addProp(eLFO2InternalDepth); - addProp(eLFO2ControlDepth); - { - char* choices[] = { "internal", "modwheel", "foot", - "internal+modwheel", "internal+foot", 0 }; - static const gig::lfo2_ctrl_t values[] = { - gig::lfo2_ctrl_internal, - gig::lfo2_ctrl_modwheel, - gig::lfo2_ctrl_foot, - gig::lfo2_ctrl_internal_modwheel, - gig::lfo2_ctrl_internal_foot - }; - eLFO2Controller.set_choices(choices, values); - } - addProp(eLFO2Controller); - addProp(eLFO2FlipPhase); - addProp(eLFO2Sync); - - nextPage(); - - addHeader("EG3"); - addProp(eEG3Attack); - addProp(eEG3Depth); - addHeader("LFO3"); - addProp(eLFO3Frequency); - addProp(eLFO3InternalDepth); - addProp(eLFO3ControlDepth); - { - char* choices[] = { "internal", "modwheel", "aftertouch", - "internal+modwheel", "internal+aftertouch", 0 }; - static const gig::lfo3_ctrl_t values[] = { - gig::lfo3_ctrl_internal, - gig::lfo3_ctrl_modwheel, - gig::lfo3_ctrl_aftertouch, - gig::lfo3_ctrl_internal_modwheel, - gig::lfo3_ctrl_internal_aftertouch - }; - eLFO3Controller.set_choices(choices, values); - } - addProp(eLFO3Controller); - addProp(eLFO3Sync); - addHeader("VCF"); - addProp(eVCFEnabled); - { - char* choices[] = { "lowpass", "lowpassturbo", "bandpass", - "highpass", "bandreject", 0 }; - static const gig::vcf_type_t values[] = { - gig::vcf_type_lowpass, - gig::vcf_type_lowpassturbo, - gig::vcf_type_bandpass, - gig::vcf_type_highpass, - gig::vcf_type_bandreject - }; - eVCFType.set_choices(choices, values); - } - addProp(eVCFType); - { - char* choices[] = { "none", "none2", "modwheel", "effect1", "effect2", - "breath", "foot", "sustainpedal", "softpedal", - "genpurpose7", "genpurpose8", "aftertouch", 0 }; - static const gig::vcf_cutoff_ctrl_t values[] = { - gig::vcf_cutoff_ctrl_none, - gig::vcf_cutoff_ctrl_none2, - gig::vcf_cutoff_ctrl_modwheel, - gig::vcf_cutoff_ctrl_effect1, - gig::vcf_cutoff_ctrl_effect2, - gig::vcf_cutoff_ctrl_breath, - gig::vcf_cutoff_ctrl_foot, - gig::vcf_cutoff_ctrl_sustainpedal, - gig::vcf_cutoff_ctrl_softpedal, - gig::vcf_cutoff_ctrl_genpurpose7, - gig::vcf_cutoff_ctrl_genpurpose8, - gig::vcf_cutoff_ctrl_aftertouch - }; - eVCFCutoffController.set_choices(choices, values); - } - addProp(eVCFCutoffController); - addProp(eVCFCutoffControllerInvert); - addProp(eVCFCutoff); - char* curve_type_texts[] = { "nonlinear", "linear", "special", 0 }; - static const gig::curve_type_t curve_type_values[] = { - gig::curve_type_nonlinear, - gig::curve_type_linear, - gig::curve_type_special - }; - eVCFVelocityCurve.set_choices(curve_type_texts, curve_type_values); - addProp(eVCFVelocityCurve); - addProp(eVCFVelocityScale); - addProp(eVCFVelocityDynamicRange); - addProp(eVCFResonance); - addProp(eVCFResonanceDynamic); - { - char* choices[] = { "none", "genpurpose3", "genpurpose4", - "genpurpose5", "genpurpose6", 0 }; - static const gig::vcf_res_ctrl_t values[] = { - gig::vcf_res_ctrl_none, - gig::vcf_res_ctrl_genpurpose3, - gig::vcf_res_ctrl_genpurpose4, - gig::vcf_res_ctrl_genpurpose5, - gig::vcf_res_ctrl_genpurpose6 - }; - eVCFResonanceController.set_choices(choices, values); - } - addProp(eVCFResonanceController); - addProp(eVCFKeyboardTracking); - addProp(eVCFKeyboardTrackingBreakpoint); - - nextPage(); - - eVelocityResponseCurve.set_choices(curve_type_texts, curve_type_values); - addProp(eVelocityResponseCurve); - addProp(eVelocityResponseDepth); - addProp(eVelocityResponseCurveScaling); - eReleaseVelocityResponseCurve.set_choices(curve_type_texts, - curve_type_values); - addProp(eReleaseVelocityResponseCurve); - addProp(eReleaseVelocityResponseDepth); - addProp(eReleaseTriggerDecay); - addProp(eCrossfade_in_start); - addProp(eCrossfade_in_end); - addProp(eCrossfade_out_start); - addProp(eCrossfade_out_end); - addProp(ePitchTrack); - { - char* choices[] = { "none", "effect4depth", "effect5depth", 0 }; - static const gig::dim_bypass_ctrl_t values[] = { - gig::dim_bypass_ctrl_none, - gig::dim_bypass_ctrl_94, - gig::dim_bypass_ctrl_95 - }; - eDimensionBypass.set_choices(choices, values); - } - addProp(eDimensionBypass); - addProp(ePan); - addProp(eSelfMask); - addProp(eAttenuationController); - addProp(eInvertAttenuationController); - addProp(eAttenuationControllerThreshold); - addProp(eChannelOffset); - addProp(eSustainDefeat); - - nextPage(); - addProp(eMSDecode); - addProp(eSampleStartOffset); - addProp(eUnityNote); - addProp(eFineTune); - addProp(eGain); - addProp(eSampleLoops); - nextPage(); - - eEG1InfiniteSustain.signal_toggled().connect( - sigc::mem_fun(*this, &MainWindow::EG1InfiniteSustain_toggled) ); - eEG2InfiniteSustain.signal_toggled().connect( - sigc::mem_fun(*this, &MainWindow::EG2InfiniteSustain_toggled) ); - eEG1Controller.signal_changed().connect( - sigc::mem_fun(*this, &MainWindow::EG1Controller_changed) ); - eEG2Controller.signal_changed().connect( - sigc::mem_fun(*this, &MainWindow::EG2Controller_changed) ); - eLFO1Controller.signal_changed().connect( - sigc::mem_fun(*this, &MainWindow::LFO1Controller_changed) ); - eLFO2Controller.signal_changed().connect( - sigc::mem_fun(*this, &MainWindow::LFO2Controller_changed) ); - eLFO3Controller.signal_changed().connect( - sigc::mem_fun(*this, &MainWindow::LFO3Controller_changed) ); - eAttenuationController.signal_changed().connect( - sigc::mem_fun(*this, &MainWindow::AttenuationController_changed) ); - eVCFEnabled.signal_toggled().connect( - sigc::mem_fun(*this, &MainWindow::VCFEnabled_toggled) ); - eVCFCutoffController.signal_changed().connect( - sigc::mem_fun(*this, &MainWindow::VCFCutoffController_changed) ); - eVCFResonanceController.signal_changed().connect( - sigc::mem_fun(*this, &MainWindow::VCFResonanceController_changed) ); - - eCrossfade_in_start.signal_value_changed().connect( - sigc::mem_fun(*this, &MainWindow::crossfade1_changed)); - eCrossfade_in_end.signal_value_changed().connect( - sigc::mem_fun(*this, &MainWindow::crossfade2_changed)); - eCrossfade_out_start.signal_value_changed().connect( - sigc::mem_fun(*this, &MainWindow::crossfade3_changed)); - eCrossfade_out_end.signal_value_changed().connect( - sigc::mem_fun(*this, &MainWindow::crossfade4_changed)); - - //m_Notebook.append_page(m_ScrolledWindow2, "Table"); - m_Notebook.append_page(*table[0], "EG1"); - m_Notebook.append_page(*table[1], "EG2"); - m_Notebook.append_page(*table[2], "EG3"); - m_Notebook.append_page(*table[3], "Velocity"); - m_Notebook.append_page(*table[4], "Misc"); - m_Notebook.set_size_request(400, 500); + m_ScrolledWindowSamples.add(m_TreeViewSamples); + m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + + + m_TreeViewNotebook.set_size_request(300); - m_HPaned.add1(m_ScrolledWindow); - m_HPaned.add2(m_Notebook); + m_HPaned.add1(m_TreeViewNotebook); + m_HPaned.add2(dimreg_edit); + + + m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples"); + m_TreeViewNotebook.append_page(m_ScrolledWindow, "Instruments"); actionGroup = Gtk::ActionGroup::create(); @@ -812,7 +109,7 @@ actionGroup->add(Gtk::Action::create("InstrProperties", Gtk::Stock::PROPERTIES), sigc::mem_fun( - *this, &MainWindow::on_action_file_properties)); + *this, &MainWindow::show_instr_props)); actionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT), sigc::mem_fun( *this, &MainWindow::hide)); @@ -826,10 +123,32 @@ sigc::mem_fun( *this, &MainWindow::on_action_help_about)); #endif - action = Gtk::Action::create("Remove", "Ta bort"); - actionGroup->add(action, - sigc::mem_fun( - *this, &MainWindow::hide)); + actionGroup->add( + Gtk::Action::create("AddInstrument", _("Add _Instrument")), + sigc::mem_fun(*this, &MainWindow::on_action_add_instrument) + ); + actionGroup->add( + Gtk::Action::create("RemoveInstrument", Gtk::Stock::REMOVE), + sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument) + ); + + // sample right-click popup actions + actionGroup->add( + Gtk::Action::create("SampleProperties", Gtk::Stock::PROPERTIES), + sigc::mem_fun(*this, &MainWindow::on_action_sample_properties) + ); + actionGroup->add( + Gtk::Action::create("AddGroup", _("Add _Group")), + sigc::mem_fun(*this, &MainWindow::on_action_add_group) + ); + actionGroup->add( + Gtk::Action::create("AddSample", _("Add _Sample(s)")), + sigc::mem_fun(*this, &MainWindow::on_action_add_sample) + ); + actionGroup->add( + Gtk::Action::create("RemoveSample", Gtk::Stock::REMOVE), + sigc::mem_fun(*this, &MainWindow::on_action_remove_sample) + ); uiManager = Gtk::UIManager::create(); uiManager->insert_action_group(actionGroup); @@ -859,7 +178,16 @@ " " " " " " - " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " " " ""; uiManager->add_ui_from_string(ui_info); @@ -881,11 +209,39 @@ // Create the Tree model: m_refTreeModel = Gtk::ListStore::create(m_Columns); m_TreeView.set_model(m_refTreeModel); + m_refTreeModel->signal_row_changed().connect( + sigc::mem_fun(*this, &MainWindow::instrument_name_changed) + ); // Add the TreeView's view columns: - m_TreeView.append_column("Instrument", m_Columns.m_col_name); + m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name); m_TreeView.set_headers_visible(false); + // create samples treeview (including its data model) + m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel); + m_TreeViewSamples.set_model(m_refSamplesTreeModel); + // m_TreeViewSamples.set_reorderable(); + m_TreeViewSamples.append_column_editable("Samples", m_SamplesModel.m_col_name); + m_TreeViewSamples.set_headers_visible(false); + m_TreeViewSamples.signal_button_press_event().connect_notify( + sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release) + ); + m_refSamplesTreeModel->signal_row_changed().connect( + sigc::mem_fun(*this, &MainWindow::sample_name_changed) + ); + + // establish drag&drop between samples tree view and dimension region 'Sample' text entry + std::list drag_target_gig_sample; + drag_target_gig_sample.push_back( Gtk::TargetEntry("gig::Sample") ); + m_TreeViewSamples.drag_source_set(drag_target_gig_sample); + m_TreeViewSamples.signal_drag_data_get().connect( + sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get) + ); + dimreg_edit.wSample->drag_dest_set(drag_target_gig_sample); + dimreg_edit.wSample->signal_drag_data_received().connect( + sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received) + ); + file = 0; show_all_children(); @@ -902,7 +258,7 @@ void MainWindow::dimreg_changed() { - set_dim_region(m_DimRegionChooser.get_dimregion()); + dimreg_edit.set_dim_region(m_DimRegionChooser.get_dimregion()); } void MainWindow::on_sel_change() @@ -920,249 +276,6 @@ } } -void MainWindow::set_dim_region(gig::DimensionRegion* d) -{ - update_gui = false; - wSample->set_text(d->pSample ? d->pSample->pInfo->Name.c_str() : "NULL"); - eVelocityUpperLimit.set_dimreg(d); - eEG1PreAttack.set_dimreg(d); - eEG1Attack.set_dimreg(d); - eEG1Decay1.set_dimreg(d); - eEG1Decay2.set_dimreg(d); - eEG1InfiniteSustain.set_dimreg(d); - eEG1Sustain.set_dimreg(d); - eEG1Release.set_dimreg(d); - eEG1Hold.set_dimreg(d); - eEG1Controller.set_dimreg(d); - eEG1ControllerInvert.set_dimreg(d); - eEG1ControllerAttackInfluence.set_dimreg(d); - eEG1ControllerDecayInfluence.set_dimreg(d); - eEG1ControllerReleaseInfluence.set_dimreg(d); - eLFO1Frequency.set_dimreg(d); - eLFO1InternalDepth.set_dimreg(d); - eLFO1ControlDepth.set_dimreg(d); - eLFO1Controller.set_dimreg(d); - eLFO1FlipPhase.set_dimreg(d); - eLFO1Sync.set_dimreg(d); - eEG2PreAttack.set_dimreg(d); - eEG2Attack.set_dimreg(d); - eEG2Decay1.set_dimreg(d); - eEG2Decay2.set_dimreg(d); - eEG2InfiniteSustain.set_dimreg(d); - eEG2Sustain.set_dimreg(d); - eEG2Release.set_dimreg(d); - eEG2Controller.set_dimreg(d); - eEG2ControllerInvert.set_dimreg(d); - eEG2ControllerAttackInfluence.set_dimreg(d); - eEG2ControllerDecayInfluence.set_dimreg(d); - eEG2ControllerReleaseInfluence.set_dimreg(d); - eLFO2Frequency.set_dimreg(d); - eLFO2InternalDepth.set_dimreg(d); - eLFO2ControlDepth.set_dimreg(d); - eLFO2Controller.set_dimreg(d); - eLFO2FlipPhase.set_dimreg(d); - eLFO2Sync.set_dimreg(d); - eEG3Attack.set_dimreg(d); - eEG3Depth.set_dimreg(d); - eLFO3Frequency.set_dimreg(d); - eLFO3InternalDepth.set_dimreg(d); - eLFO3ControlDepth.set_dimreg(d); - eLFO3Controller.set_dimreg(d); - eLFO3Sync.set_dimreg(d); - eVCFEnabled.set_dimreg(d); - eVCFType.set_dimreg(d); - eVCFCutoffController.set_dimreg(d); - eVCFCutoffControllerInvert.set_dimreg(d); - eVCFCutoff.set_dimreg(d); - eVCFVelocityCurve.set_dimreg(d); - eVCFVelocityScale.set_dimreg(d); - eVCFVelocityDynamicRange.set_dimreg(d); - eVCFResonance.set_dimreg(d); - eVCFResonanceDynamic.set_dimreg(d); - eVCFResonanceController.set_dimreg(d); - eVCFKeyboardTracking.set_dimreg(d); - eVCFKeyboardTrackingBreakpoint.set_dimreg(d); - eVelocityResponseCurve.set_dimreg(d); - eVelocityResponseDepth.set_dimreg(d); - eVelocityResponseCurveScaling.set_dimreg(d); - eReleaseVelocityResponseCurve.set_dimreg(d); - eReleaseVelocityResponseDepth.set_dimreg(d); - eReleaseTriggerDecay.set_dimreg(d); - eCrossfade_in_start.set_dimreg(d); - eCrossfade_in_end.set_dimreg(d); - eCrossfade_out_start.set_dimreg(d); - eCrossfade_out_end.set_dimreg(d); - ePitchTrack.set_dimreg(d); - eDimensionBypass.set_dimreg(d); - ePan.set_dimreg(d); - eSelfMask.set_dimreg(d); - eAttenuationController.set_dimreg(d); - eInvertAttenuationController.set_dimreg(d); - eAttenuationControllerThreshold.set_dimreg(d); - eChannelOffset.set_dimreg(d); - eSustainDefeat.set_dimreg(d); - eMSDecode.set_dimreg(d); - eSampleStartOffset.set_dimreg(d); - eUnityNote.set_dimreg(d); - eFineTune.set_dimreg(d); - eGain.set_dimreg(d); - eSampleLoops.set_dimreg(d); - - VCFEnabled_toggled(); - - update_gui = true; -} - -void MainWindow::VCFEnabled_toggled() -{ - bool sensitive = eVCFEnabled.get_active(); - eVCFType.set_sensitive(sensitive); - eVCFCutoffController.set_sensitive(sensitive); - eVCFVelocityCurve.set_sensitive(sensitive); - eVCFVelocityScale.set_sensitive(sensitive); - eVCFVelocityDynamicRange.set_sensitive(sensitive); - eVCFResonance.set_sensitive(sensitive); - eVCFResonanceController.set_sensitive(sensitive); - eVCFKeyboardTracking.set_sensitive(sensitive); - eVCFKeyboardTrackingBreakpoint.set_sensitive(sensitive); - eEG2PreAttack.set_sensitive(sensitive); - eEG2Attack.set_sensitive(sensitive); - eEG2Decay1.set_sensitive(sensitive); - eEG2InfiniteSustain.set_sensitive(sensitive); - eEG2Sustain.set_sensitive(sensitive); - eEG2Release.set_sensitive(sensitive); - eEG2Controller.set_sensitive(sensitive); - eEG2ControllerAttackInfluence.set_sensitive(sensitive); - eEG2ControllerDecayInfluence.set_sensitive(sensitive); - eEG2ControllerReleaseInfluence.set_sensitive(sensitive); - eLFO2Frequency.set_sensitive(sensitive); - eLFO2InternalDepth.set_sensitive(sensitive); - eLFO2ControlDepth.set_sensitive(sensitive); - eLFO2Controller.set_sensitive(sensitive); - eLFO2FlipPhase.set_sensitive(sensitive); - eLFO2Sync.set_sensitive(sensitive); - if (sensitive) { - VCFCutoffController_changed(); - VCFResonanceController_changed(); - EG2InfiniteSustain_toggled(); - EG2Controller_changed(); - LFO2Controller_changed(); - } else { - eVCFCutoffControllerInvert.set_sensitive(false); - eVCFCutoff.set_sensitive(false); - eVCFResonanceDynamic.set_sensitive(false); - eVCFResonance.set_sensitive(false); - eEG2Decay2.set_sensitive(false); - eEG2ControllerInvert.set_sensitive(false); - eLFO2InternalDepth.set_sensitive(false); - eLFO2ControlDepth.set_sensitive(false); - } -} - -void MainWindow::VCFCutoffController_changed() -{ - int rowno = eVCFCutoffController.get_active_row_number(); - bool hasController = rowno != 0 && rowno != 1; - - eVCFCutoffControllerInvert.set_sensitive(hasController); - eVCFCutoff.set_sensitive(!hasController); - eVCFResonanceDynamic.set_sensitive(!hasController); - eVCFVelocityScale.label.set_text(hasController ? "MinimumCutoff:" : - "VelocityScale:"); -} - -void MainWindow::VCFResonanceController_changed() -{ - bool hasController = eVCFResonanceController.get_active_row_number() != 0; - eVCFResonance.set_sensitive(!hasController); -} - -void MainWindow::EG1InfiniteSustain_toggled() -{ - bool infSus = eEG1InfiniteSustain.get_active(); - eEG1Decay2.set_sensitive(!infSus); -} - -void MainWindow::EG2InfiniteSustain_toggled() -{ - bool infSus = eEG2InfiniteSustain.get_active(); - eEG2Decay2.set_sensitive(!infSus); -} - -void MainWindow::EG1Controller_changed() -{ - bool hasController = eEG1Controller.get_active_row_number() != 0; - eEG1ControllerInvert.set_sensitive(hasController); -} - -void MainWindow::EG2Controller_changed() -{ - bool hasController = eEG2Controller.get_active_row_number() != 0; - eEG2ControllerInvert.set_sensitive(hasController); -} - -void MainWindow::AttenuationController_changed() -{ - bool hasController = eAttenuationController.get_active_row_number() != 0; - eInvertAttenuationController.set_sensitive(hasController); -} - -void MainWindow::LFO1Controller_changed() -{ - int rowno = eLFO1Controller.get_active_row_number(); - eLFO1ControlDepth.set_sensitive(rowno != 0); - eLFO1InternalDepth.set_sensitive(rowno != 1 && rowno != 2); -} - -void MainWindow::LFO2Controller_changed() -{ - int rowno = eLFO2Controller.get_active_row_number(); - eLFO2ControlDepth.set_sensitive(rowno != 0); - eLFO2InternalDepth.set_sensitive(rowno != 1 && rowno != 2); -} - -void MainWindow::LFO3Controller_changed() -{ - int rowno = eLFO3Controller.get_active_row_number(); - eLFO3ControlDepth.set_sensitive(rowno != 0); - eLFO3InternalDepth.set_sensitive(rowno != 1 && rowno != 2); -} - -void MainWindow::crossfade1_changed() -{ - double c1 = eCrossfade_in_start.get_value(); - double c2 = eCrossfade_in_end.get_value(); - if (c1 > c2) eCrossfade_in_end.set_value(c1); -} - -void MainWindow::crossfade2_changed() -{ - double c1 = eCrossfade_in_start.get_value(); - double c2 = eCrossfade_in_end.get_value(); - double c3 = eCrossfade_out_start.get_value(); - - if (c2 < c1) eCrossfade_in_start.set_value(c2); - if (c2 > c3) eCrossfade_out_start.set_value(c2); -} - -void MainWindow::crossfade3_changed() -{ - double c2 = eCrossfade_in_end.get_value(); - double c3 = eCrossfade_out_start.get_value(); - double c4 = eCrossfade_out_end.get_value(); - - if (c3 < c2) eCrossfade_in_end.set_value(c3); - if (c3 > c4) eCrossfade_out_end.set_value(c3); -} - -void MainWindow::crossfade4_changed() -{ - double c3 = eCrossfade_out_start.get_value(); - double c4 = eCrossfade_out_end.get_value(); - - if (c4 < c3) eCrossfade_out_start.set_value(c4); -} - void loader_progress_callback(gig::progress_t* progress) { Loader* loader = static_cast(progress->custom); @@ -1224,14 +337,47 @@ return finished_dispatcher; } -LoadDialog::LoadDialog() +LoadDialog::LoadDialog(const Glib::ustring& title, Gtk::Window& parent) + : Gtk::Dialog(title, parent, true) { get_vbox()->pack_start(progressBar); show_all_children(); } +// Clear all GUI elements / controls. This method is typically called +// before a new .gig file is to be created or to be loaded. +void MainWindow::__clear() { + // remove all entries from "Instrument" menu + Gtk::MenuItem* instrument_menu = + dynamic_cast(uiManager->get_widget("/MenuBar/MenuInstrument")); + instrument_menu->hide(); + for (int i = 0; i < instrument_menu->get_submenu()->items().size(); i++) { + delete &instrument_menu->get_submenu()->items()[i]; + } + instrument_menu->get_submenu()->items().clear(); + // forget all samples that ought to be imported + m_SampleImportQueue.clear(); + // clear the samples and instruments tree views + m_refTreeModel->clear(); + m_refSamplesTreeModel->clear(); + // free libgig's gig::File instance + if (file) { + delete file; + file = NULL; + } +} + void MainWindow::on_action_file_new() { + // clear all GUI elements + __clear(); + // create a new .gig file (virtually yet) + gig::File* pFile = new gig::File; + // already add one new instrument by default + gig::Instrument* pInstrument = pFile->AddInstrument(); + pInstrument->pInfo->Name = "Unnamed Instrument"; + // update GUI with that new gig::File + load_gig(pFile, NULL /*no file name yet*/); } void MainWindow::on_action_file_open() @@ -1244,34 +390,24 @@ dialog.set_filter(filter); if (dialog.run() == Gtk::RESPONSE_OK) { printf("filename=%s\n", dialog.get_filename().c_str()); - - // remove all entries from "Instrument" menu - Gtk::MenuItem* instrument_menu = - dynamic_cast(uiManager->get_widget("/MenuBar/MenuInstrument")); - instrument_menu->hide(); - for (int i = 0; i < instrument_menu->get_submenu()->items().size(); i++) { - delete &instrument_menu->get_submenu()->items()[i]; - } - instrument_menu->get_submenu()->items().clear(); - - m_refTreeModel->clear(); - if (file) delete file; - - // getInfo(dialog.get_filename().c_str(), *this); - + __clear(); printf("on_action_file_open self=%x\n", Glib::Thread::self()); - load_dialog = new LoadDialog(); // Gtk::Dialog("Loading...", *this, true); - load_dialog->show_all(); - loader = new Loader(strdup(dialog.get_filename().c_str())); - loader->signal_progress().connect( - sigc::mem_fun(*this, &MainWindow::on_loader_progress)); - loader->signal_finished().connect( - sigc::mem_fun(*this, &MainWindow::on_loader_finished)); - - loader->launch(); + load_file(dialog.get_filename().c_str()); } } +void MainWindow::load_file(const char* name) +{ + load_dialog = new LoadDialog("Loading...", *this); + load_dialog->show_all(); + loader = new Loader(strdup(name)); + loader->signal_progress().connect( + sigc::mem_fun(*this, &MainWindow::on_loader_progress)); + loader->signal_finished().connect( + sigc::mem_fun(*this, &MainWindow::on_loader_finished)); + loader->launch(); +} + void MainWindow::on_loader_progress() { load_dialog->set_fraction(loader->get_progress()); @@ -1283,7 +419,6 @@ printf("on_loader_finished self=%x\n", Glib::Thread::self()); load_gig(loader->gig, loader->filename); - Glib::RefPtr tree_sel_ref = m_TreeView.get_selection(); tree_sel_ref->select(Gtk::TreePath("0")); @@ -1292,10 +427,23 @@ void MainWindow::on_action_file_save() { + if (!file) return; + std::cout << "Saving file\n" << std::flush; + try { + file->Save(); + } catch (RIFF::Exception e) { + Glib::ustring txt = "Could not save file: " + e.Message; + Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); + msg.run(); + return; + } + std::cout << "Saving file done\n" << std::flush; + __import_queued_samples(); } void MainWindow::on_action_file_save_as() { + if (!file) return; Gtk::FileChooserDialog dialog(*this, "Open", Gtk::FILE_CHOOSER_ACTION_SAVE); dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); @@ -1304,7 +452,97 @@ dialog.set_filter(filter); if (dialog.run() == Gtk::RESPONSE_OK) { printf("filename=%s\n", dialog.get_filename().c_str()); - file->Save(dialog.get_filename()); + try { + file->Save(dialog.get_filename()); + } catch (RIFF::Exception e) { + Glib::ustring txt = "Could not save file: " + e.Message; + Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); + msg.run(); + return; + } + __import_queued_samples(); + } +} + +// actually write the sample(s)' data to the gig file +void MainWindow::__import_queued_samples() { + std::cout << "Starting sample import\n" << std::flush; + Glib::ustring error_files; + printf("Samples to import: %d\n", m_SampleImportQueue.size()); + for (std::list::iterator iter = m_SampleImportQueue.begin(); + iter != m_SampleImportQueue.end(); ) { + printf("Importing sample %s\n",(*iter).sample_path.c_str()); + SF_INFO info; + info.format = 0; + SNDFILE* hFile = sf_open((*iter).sample_path.c_str(), SFM_READ, &info); + try { + if (!hFile) throw std::string("could not open file"); + // determine sample's bit depth + int bitdepth; + switch (info.format & 0xff) { + case SF_FORMAT_PCM_S8: + bitdepth = 16; // we simply convert to 16 bit for now + break; + case SF_FORMAT_PCM_16: + bitdepth = 16; + break; + case SF_FORMAT_PCM_24: + bitdepth = 32; // we simply convert to 32 bit for now + break; + case SF_FORMAT_PCM_32: + bitdepth = 32; + break; + case SF_FORMAT_PCM_U8: + bitdepth = 16; // we simply convert to 16 bit for now + break; + case SF_FORMAT_FLOAT: + bitdepth = 32; + break; + case SF_FORMAT_DOUBLE: + bitdepth = 32; // I guess we will always truncate this to 32 bit + break; + default: + sf_close(hFile); // close sound file + throw std::string("format not supported"); // unsupported subformat (yet?) + } + // allocate appropriate copy buffer (TODO: for now we copy + // it in one piece, might be tough for very long samples) + // and copy sample data into buffer + int8_t* buffer = NULL; + switch (bitdepth) { + case 16: + buffer = new int8_t[2 * info.channels * info.frames]; + // libsndfile does the conversion for us (if needed) + sf_readf_short(hFile, (short*) buffer, info.frames); + break; + case 32: + buffer = new int8_t[4 * info.channels * info.frames]; + // libsndfile does the conversion for us (if needed) + sf_readf_int(hFile, (int*) buffer, info.frames); + break; + } + // write from buffer directly (physically) into .gig file + (*iter).gig_sample->Write(buffer, info.frames); + // cleanup + sf_close(hFile); + delete[] buffer; + // on success we remove the sample from the import queue, + // otherwise keep it, maybe it works the next time ? + std::list::iterator cur = iter; + ++iter; + m_SampleImportQueue.erase(cur); + } catch (std::string what) { + // remember the files that made trouble (and their cause) + if (error_files.size()) error_files += "\n"; + error_files += (*iter).sample_path += " (" + what + ")"; + ++iter; + } + } + // show error message box when some sample(s) could not be imported + if (error_files.size()) { + Glib::ustring txt = "Could not import the following sample(s):\n" + error_files; + Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); + msg.run(); } } @@ -1436,6 +674,7 @@ show_all_children(); } +extern char* notes[]; void InstrumentProps::set_instrument(gig::Instrument* instrument) { @@ -1467,22 +706,17 @@ entry[entryIdx].set_text(buf); } -void MainWindow::getInfo(const char *filename) -{ - RIFF::File* riff = new RIFF::File(filename); - gig::File* gig = new gig::File(riff); - - load_gig(gig, filename); -} - void MainWindow::load_gig(gig::File* gig, const char* filename) { file = gig; - const char *basename = strrchr(filename, '/'); - basename = basename ? basename + 1 : filename; - - set_title(basename); + if (filename) { + const char *basename = strrchr(filename, '/'); + basename = basename ? basename + 1 : filename; + set_title(basename); + } else { + set_title("unnamed"); + } propDialog.set_info(gig->pInfo); @@ -1490,6 +724,7 @@ dynamic_cast(uiManager->get_widget("/MenuBar/MenuInstrument")); int instrument_index = 0; + Gtk::RadioMenuItem::Group instrument_group; for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ; instrument = gig->GetNextInstrument()) { Gtk::TreeModel::iterator iter = m_refTreeModel->append(); @@ -1497,7 +732,8 @@ row[m_Columns.m_col_name] = instrument->pInfo->Name.c_str(); row[m_Columns.m_col_instr] = instrument; // create a menu item for this instrument - Gtk::MenuItem* item= new Gtk::MenuItem(instrument->pInfo->Name.c_str()); + Gtk::RadioMenuItem* item = + new Gtk::RadioMenuItem(instrument_group, instrument->pInfo->Name.c_str()); instrument_menu->get_submenu()->append(*item); item->signal_activate().connect( sigc::bind( @@ -1509,23 +745,47 @@ } instrument_menu->show(); instrument_menu->get_submenu()->show_all_children(); + + for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) { + if (group->Name != "") { + Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append(); + Gtk::TreeModel::Row rowGroup = *iterGroup; + rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str(); + rowGroup[m_SamplesModel.m_col_group] = group; + rowGroup[m_SamplesModel.m_col_sample] = NULL; + for (gig::Sample* sample = group->GetFirstSample(); + sample; sample = group->GetNextSample()) { + Gtk::TreeModel::iterator iterSample = + m_refSamplesTreeModel->append(rowGroup.children()); + Gtk::TreeModel::Row rowSample = *iterSample; + rowSample[m_SamplesModel.m_col_name] = sample->pInfo->Name.c_str(); + rowSample[m_SamplesModel.m_col_sample] = sample; + rowSample[m_SamplesModel.m_col_group] = NULL; + } + } + } } -void MainWindow::on_button_release(GdkEventButton* button) +void MainWindow::show_instr_props() { - if (button->type == GDK_2BUTTON_PRESS) { - Glib::RefPtr tree_sel_ref = m_TreeView.get_selection(); - Gtk::TreeModel::iterator it = tree_sel_ref->get_selected(); - if (it) + Glib::RefPtr tree_sel_ref = m_TreeView.get_selection(); + Gtk::TreeModel::iterator it = tree_sel_ref->get_selected(); + if (it) + { + Gtk::TreeModel::Row row = *it; + if (row[m_Columns.m_col_instr]) { - Gtk::TreeModel::Row row = *it; - if (row[m_Columns.m_col_instr]) - { - instrumentProps.set_instrument(row[m_Columns.m_col_instr]); - instrumentProps.show(); - instrumentProps.deiconify(); - } + instrumentProps.set_instrument(row[m_Columns.m_col_instr]); + instrumentProps.show(); + instrumentProps.deiconify(); } + } +} + +void MainWindow::on_button_release(GdkEventButton* button) +{ + if (button->type == GDK_2BUTTON_PRESS) { + show_instr_props(); } else if (button->type == GDK_BUTTON_PRESS && button->button == 3) { popup_menu->popup(button->button, button->time); } @@ -1534,3 +794,329 @@ void MainWindow::on_instrument_selection_change(int index) { m_RegionChooser.set_instrument(file->GetInstrument(index)); } + +void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) { + if (button->type == GDK_BUTTON_PRESS && button->button == 3) { + Gtk::Menu* sample_popup = + dynamic_cast(uiManager->get_widget("/SamplePopupMenu")); + // update enabled/disabled state of sample popup items + Glib::RefPtr sel = m_TreeViewSamples.get_selection(); + Gtk::TreeModel::iterator it = sel->get_selected(); + bool group_selected = false; + bool sample_selected = false; + if (it) { + Gtk::TreeModel::Row row = *it; + group_selected = row[m_SamplesModel.m_col_group]; + sample_selected = row[m_SamplesModel.m_col_sample]; + } + dynamic_cast(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))-> + set_sensitive(group_selected || sample_selected); + dynamic_cast(uiManager->get_widget("/SamplePopupMenu/AddSample"))-> + set_sensitive(group_selected || sample_selected); + dynamic_cast(uiManager->get_widget("/SamplePopupMenu/AddGroup"))-> + set_sensitive(file); + dynamic_cast(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))-> + set_sensitive(group_selected || sample_selected); + // show sample popup + sample_popup->popup(button->button, button->time); + } +} + +void MainWindow::on_action_add_instrument() { + static int __instrument_indexer = 0; + if (!file) return; + gig::Instrument* instrument = file->AddInstrument(); + __instrument_indexer++; + instrument->pInfo->Name = + "Unnamed Instrument " + ToString(__instrument_indexer); + // update instrument tree view + Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append(); + Gtk::TreeModel::Row rowInstr = *iterInstr; + rowInstr[m_Columns.m_col_name] = instrument->pInfo->Name.c_str(); + rowInstr[m_Columns.m_col_instr] = instrument; +} + +void MainWindow::on_action_remove_instrument() { + if (!file) return; + Glib::RefPtr sel = m_TreeView.get_selection(); + Gtk::TreeModel::iterator it = sel->get_selected(); + if (it) { + Gtk::TreeModel::Row row = *it; + gig::Instrument* instr = row[m_Columns.m_col_instr]; + try { + // remove instrument from the gig file + if (instr) file->DeleteInstrument(instr); + // remove respective row from instruments tree view + m_refTreeModel->erase(it); + } catch (RIFF::Exception e) { + Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR); + msg.run(); + } + } +} + +void MainWindow::on_action_sample_properties() { + //TODO: show a dialog where the selected sample's properties can be edited + Gtk::MessageDialog msg( + *this, "Sorry, yet to be implemented!", false, Gtk::MESSAGE_INFO + ); + msg.run(); +} + +void MainWindow::on_action_add_group() { + static int __sample_indexer = 0; + if (!file) return; + gig::Group* group = file->AddGroup(); + group->Name = "Unnamed Group"; + if (__sample_indexer) group->Name += " " + ToString(__sample_indexer); + __sample_indexer++; + // update sample tree view + Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append(); + Gtk::TreeModel::Row rowGroup = *iterGroup; + rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str(); + rowGroup[m_SamplesModel.m_col_sample] = NULL; + rowGroup[m_SamplesModel.m_col_group] = group; +} + +void MainWindow::on_action_add_sample() { + if (!file) return; + // get selected group + Glib::RefPtr sel = m_TreeViewSamples.get_selection(); + Gtk::TreeModel::iterator it = sel->get_selected(); + if (!it) return; + Gtk::TreeModel::Row row = *it; + gig::Group* group = row[m_SamplesModel.m_col_group]; + if (!group) { // not a group, but a sample is selected (probably) + gig::Sample* sample = row[m_SamplesModel.m_col_sample]; + if (!sample) return; + it = row.parent(); // resolve parent (that is the sample's group) + if (!it) return; + row = *it; + group = row[m_SamplesModel.m_col_group]; + if (!group) return; + } + // show 'browse for file' dialog + Gtk::FileChooserDialog dialog(*this, _("Add Sample(s)")); + dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); + dialog.set_select_multiple(true); + Gtk::FileFilter soundfilter; // matches all file types supported by libsndfile + const char* supportedFileTypes[] = { + "*.wav", "*.WAV", "*.aiff", "*.AIFF", "*.aifc", "*.AIFC", "*.snd", + "*.SND", "*.au", "*.AU", "*.paf", "*.PAF", "*.iff", "*.IFF", + "*.svx", "*.SVX", "*.sf", "*.SF", "*.voc", "*.VOC", "*.w64", + "*.W64", "*.pvf", "*.PVF", "*.xi", "*.XI", "*.htk", "*.HTK", + "*.caf", "*.CAF", NULL + }; + for (int i = 0; supportedFileTypes[i]; i++) + soundfilter.add_pattern(supportedFileTypes[i]); + soundfilter.set_name("Sound Files"); + Gtk::FileFilter allpassfilter; // matches every file + allpassfilter.add_pattern("*.*"); + allpassfilter.set_name("All Files"); + dialog.add_filter(soundfilter); + dialog.add_filter(allpassfilter); + if (dialog.run() == Gtk::RESPONSE_OK) { + Glib::ustring error_files; + Glib::SListHandle filenames = dialog.get_filenames(); + for (Glib::SListHandle::iterator iter = filenames.begin(); + iter != filenames.end(); ++iter) { + printf("Adding sample %s\n",(*iter).c_str()); + // use libsndfile to retrieve file informations + SF_INFO info; + info.format = 0; + SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info); + try { + if (!hFile) throw std::string("could not open file"); + int bitdepth; + switch (info.format & 0xff) { + case SF_FORMAT_PCM_S8: + bitdepth = 16; // we simply convert to 16 bit for now + break; + case SF_FORMAT_PCM_16: + bitdepth = 16; + break; + case SF_FORMAT_PCM_24: + bitdepth = 32; // we simply convert to 32 bit for now + break; + case SF_FORMAT_PCM_32: + bitdepth = 32; + break; + case SF_FORMAT_PCM_U8: + bitdepth = 16; // we simply convert to 16 bit for now + break; + case SF_FORMAT_FLOAT: + bitdepth = 32; + break; + case SF_FORMAT_DOUBLE: + bitdepth = 32; // I guess we will always truncate this to 32 bit + break; + default: + sf_close(hFile); // close sound file + throw std::string("format not supported"); // unsupported subformat (yet?) + } + // add a new sample to the .gig file + gig::Sample* sample = file->AddSample(); + // file name without path + sample->pInfo->Name = (*iter).substr((*iter).rfind('/') + 1).raw(); + sample->Channels = info.channels; + sample->BitDepth = bitdepth; + sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels; + sample->SamplesPerSecond = info.samplerate; + // schedule resizing the sample (which will be done + // physically when File::Save() is called) + sample->Resize(info.frames); + // make sure sample is part of the selected group + group->AddSample(sample); + // schedule that physical resize and sample import + // (data copying), performed when "Save" is requested + SampleImportItem sched_item; + sched_item.gig_sample = sample; + sched_item.sample_path = *iter; + m_SampleImportQueue.push_back(sched_item); + // add sample to the tree view + Gtk::TreeModel::iterator iterSample = + m_refSamplesTreeModel->append(row.children()); + Gtk::TreeModel::Row rowSample = *iterSample; + rowSample[m_SamplesModel.m_col_name] = sample->pInfo->Name.c_str(); + rowSample[m_SamplesModel.m_col_sample] = sample; + rowSample[m_SamplesModel.m_col_group] = NULL; + // close sound file + sf_close(hFile); + } catch (std::string what) { // remember the files that made trouble (and their cause) + if (error_files.size()) error_files += "\n"; + error_files += *iter += " (" + what + ")"; + } + } + // show error message box when some file(s) could not be opened / added + if (error_files.size()) { + Glib::ustring txt = "Could not add the following sample(s):\n" + error_files; + Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); + msg.run(); + } + } +} + +void MainWindow::on_action_remove_sample() { + if (!file) return; + Glib::RefPtr sel = m_TreeViewSamples.get_selection(); + Gtk::TreeModel::iterator it = sel->get_selected(); + if (it) { + Gtk::TreeModel::Row row = *it; + gig::Group* group = row[m_SamplesModel.m_col_group]; + gig::Sample* sample = row[m_SamplesModel.m_col_sample]; + Glib::ustring name = row[m_SamplesModel.m_col_name]; + try { + // remove group or sample from the gig file + if (group) { + // temporarily remember the samples that bolong to + // that group (we need that to clean the queue) + std::list members; + for (gig::Sample* pSample = group->GetFirstSample(); + pSample; pSample = group->GetNextSample()) { + members.push_back(pSample); + } + // delete the group in the .gig file including the + // samples that belong to the group + file->DeleteGroup(group); + // if sample(s) were just previously added, remove + // them from the import queue + for (std::list::iterator member = members.begin(); + member != members.end(); ++member) { + for (std::list::iterator iter = m_SampleImportQueue.begin(); + iter != m_SampleImportQueue.end(); ++iter) { + if ((*iter).gig_sample == *member) { + printf("Removing previously added sample '%s' from group '%s'\n", + (*iter).sample_path.c_str(), name.c_str()); + m_SampleImportQueue.erase(iter); + break; + } + } + } + } else if (sample) { + // remove sample from the .gig file + file->DeleteSample(sample); + // if sample was just previously added, remove it from + // the import queue + for (std::list::iterator iter = m_SampleImportQueue.begin(); + iter != m_SampleImportQueue.end(); ++iter) { + if ((*iter).gig_sample == sample) { + printf("Removing previously added sample '%s'\n", + (*iter).sample_path.c_str()); + m_SampleImportQueue.erase(iter); + break; + } + } + } + // remove respective row(s) from samples tree view + m_refSamplesTreeModel->erase(it); + } catch (RIFF::Exception e) { + Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR); + msg.run(); + } + } +} + +void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr&, + Gtk::SelectionData& selection_data, guint, guint) +{ + // get selected sample + gig::Sample* sample = NULL; + Glib::RefPtr sel = m_TreeViewSamples.get_selection(); + Gtk::TreeModel::iterator it = sel->get_selected(); + if (it) { + Gtk::TreeModel::Row row = *it; + sample = row[m_SamplesModel.m_col_sample]; + } + // pass the gig::Sample as pointer + selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample, + sizeof(sample)/*length of data in bytes*/); +} + +void MainWindow::on_sample_label_drop_drag_data_received( + const Glib::RefPtr& context, int, int, + const Gtk::SelectionData& selection_data, guint, guint time) +{ + gig::DimensionRegion* dimregion = m_DimRegionChooser.get_dimregion(); + gig::Sample* sample = *((gig::Sample**) selection_data.get_data()); + + if (sample && dimregion && selection_data.get_length() == sizeof(gig::Sample*)) { + if (sample != dimregion->pSample) { + dimregion->pSample = sample; + dimreg_edit.wSample->set_text(dimregion->pSample->pInfo->Name.c_str()); + std::cout << "Drop received sample \"" << + dimregion->pSample->pInfo->Name.c_str() << "\"" << std::endl; + // drop success + context->drop_reply(true, time); + return; + } + } + // drop failed + context->drop_reply(false, time); +} + +void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path, + const Gtk::TreeModel::iterator& iter) { + if (!iter) return; + Gtk::TreeModel::Row row = *iter; + Glib::ustring name = row[m_SamplesModel.m_col_name]; + gig::Group* group = row[m_SamplesModel.m_col_group]; + gig::Sample* sample = row[m_SamplesModel.m_col_sample]; + if (group) { + group->Name = name; + std::cout << "Group name changed\n" << std::flush; + } else if (sample) { + sample->pInfo->Name = name.raw(); + std::cout << "Sample name changed\n" << std::flush; + } +} + +void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path, + const Gtk::TreeModel::iterator& iter) { + std::cout << "Instrument name changed\n" << std::flush; + if (!iter) return; + Gtk::TreeModel::Row row = *iter; + Glib::ustring name = row[m_Columns.m_col_name]; + gig::Instrument* instrument = row[m_Columns.m_col_instr]; + if (instrument) instrument->pInfo->Name = name.raw(); +}