27 |
#if GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6 |
#if GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6 |
28 |
#define ABOUT_DIALOG |
#define ABOUT_DIALOG |
29 |
#include <gtkmm/aboutdialog.h> |
#include <gtkmm/aboutdialog.h> |
30 |
|
#include <gtkmm/messagedialog.h> |
31 |
|
#include <gtkmm/targetentry.h> |
32 |
#endif |
#endif |
33 |
|
|
34 |
|
#include <stdio.h> |
35 |
|
#include <sndfile.h> |
36 |
|
|
37 |
#define _(String) gettext(String) |
#define _(String) gettext(String) |
38 |
|
|
39 |
|
template<class T> inline std::string ToString(T o) { |
40 |
|
std::stringstream ss; |
41 |
|
ss << o; |
42 |
|
return ss.str(); |
43 |
|
} |
44 |
|
|
45 |
bool update_gui; |
bool update_gui; |
46 |
|
|
391 |
|
|
392 |
MainWindow::MainWindow() : |
MainWindow::MainWindow() : |
393 |
// eSample("Sample", wSample), |
// eSample("Sample", wSample), |
|
eVelocityUpperLimit("VelocityUpperLimit", |
|
|
&gig::DimensionRegion::VelocityUpperLimit), |
|
394 |
eEG1PreAttack("PreAttack", &gig::DimensionRegion::EG1PreAttack, 0, 100, 2), |
eEG1PreAttack("PreAttack", &gig::DimensionRegion::EG1PreAttack, 0, 100, 2), |
395 |
eEG1Attack("Attack", &gig::DimensionRegion::EG1Attack, 0, 60, 3), |
eEG1Attack("Attack", &gig::DimensionRegion::EG1Attack, 0, 60, 3), |
396 |
eEG1Decay1("Decay1", &gig::DimensionRegion::EG1Decay1, 0.005, 60, 3), |
eEG1Decay1("Decay1", &gig::DimensionRegion::EG1Decay1, 0.005, 60, 3), |
519 |
eSampleLoops("SampleLoops", &access_SampleLoops, 0, 1) |
eSampleLoops("SampleLoops", &access_SampleLoops, 0, 1) |
520 |
{ |
{ |
521 |
// set_border_width(5); |
// set_border_width(5); |
522 |
set_default_size(400, 200); |
// set_default_size(400, 200); |
523 |
|
|
524 |
|
|
525 |
add(m_VBox); |
add(m_VBox); |
532 |
m_TreeView.signal_button_press_event().connect_notify( |
m_TreeView.signal_button_press_event().connect_notify( |
533 |
sigc::mem_fun(*this, &MainWindow::on_button_release)); |
sigc::mem_fun(*this, &MainWindow::on_button_release)); |
534 |
|
|
535 |
// Add the TreeView, inside a ScrolledWindow, with the button underneath: |
// Add the TreeView tab, inside a ScrolledWindow, with the button underneath: |
536 |
m_ScrolledWindow.add(m_TreeView); |
m_ScrolledWindow.add(m_TreeView); |
537 |
m_ScrolledWindow.set_size_request(400, 600); |
// m_ScrolledWindow.set_size_request(200, 600); |
538 |
m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); |
m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); |
539 |
|
|
540 |
|
m_ScrolledWindowSamples.add(m_TreeViewSamples); |
541 |
|
m_ScrolledWindowSamples.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); |
542 |
|
|
543 |
|
|
544 |
for (int i = 0 ; i < 5 ; i++) { |
for (int i = 0 ; i < 5 ; i++) { |
545 |
table[i] = new Gtk::Table(3, 1); |
table[i] = new Gtk::Table(3, 1); |
546 |
table[i]->set_col_spacings(5); |
table[i]->set_col_spacings(5); |
551 |
firstRowInBlock = 0; |
firstRowInBlock = 0; |
552 |
|
|
553 |
addString("Sample", lSample, wSample); |
addString("Sample", lSample, wSample); |
554 |
addProp(eVelocityUpperLimit); |
//TODO: the following would break drag&drop: wSample->property_editable().set_value(false); or this: wSample->set_editable(false); |
555 |
addHeader("EG1"); |
addHeader("EG1"); |
556 |
addProp(eEG1PreAttack); |
addProp(eEG1PreAttack); |
557 |
addProp(eEG1Attack); |
addProp(eEG1Attack); |
790 |
m_Notebook.append_page(*table[2], "EG3"); |
m_Notebook.append_page(*table[2], "EG3"); |
791 |
m_Notebook.append_page(*table[3], "Velocity"); |
m_Notebook.append_page(*table[3], "Velocity"); |
792 |
m_Notebook.append_page(*table[4], "Misc"); |
m_Notebook.append_page(*table[4], "Misc"); |
793 |
m_Notebook.set_size_request(400, 500); |
// m_Notebook.set_size_request(400, 500); |
794 |
|
|
795 |
m_HPaned.add1(m_ScrolledWindow); |
m_TreeViewNotebook.set_size_request(300); |
796 |
|
m_HPaned.add1(m_TreeViewNotebook); |
797 |
m_HPaned.add2(m_Notebook); |
m_HPaned.add2(m_Notebook); |
798 |
|
|
799 |
|
|
800 |
|
m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples"); |
801 |
|
m_TreeViewNotebook.append_page(m_ScrolledWindow, "Instruments"); |
802 |
|
|
803 |
|
|
804 |
actionGroup = Gtk::ActionGroup::create(); |
actionGroup = Gtk::ActionGroup::create(); |
805 |
|
|
806 |
actionGroup->add(Gtk::Action::create("MenuFile", _("_File"))); |
actionGroup->add(Gtk::Action::create("MenuFile", _("_File"))); |
834 |
actionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT), |
actionGroup->add(Gtk::Action::create("Quit", Gtk::Stock::QUIT), |
835 |
sigc::mem_fun( |
sigc::mem_fun( |
836 |
*this, &MainWindow::hide)); |
*this, &MainWindow::hide)); |
837 |
|
actionGroup->add(Gtk::Action::create("MenuInstrument", _("_Instrument"))); |
838 |
|
|
839 |
action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP); |
action = Gtk::Action::create("MenuHelp", Gtk::Stock::HELP); |
840 |
actionGroup->add(Gtk::Action::create("MenuHelp", |
actionGroup->add(Gtk::Action::create("MenuHelp", |
841 |
action->property_label())); |
action->property_label())); |
844 |
sigc::mem_fun( |
sigc::mem_fun( |
845 |
*this, &MainWindow::on_action_help_about)); |
*this, &MainWindow::on_action_help_about)); |
846 |
#endif |
#endif |
847 |
action = Gtk::Action::create("Remove", "Ta bort"); |
action = Gtk::Action::create("Remove", Gtk::Stock::REMOVE); |
848 |
actionGroup->add(action, |
actionGroup->add(action, |
849 |
sigc::mem_fun( |
sigc::mem_fun( |
850 |
*this, &MainWindow::hide)); |
*this, &MainWindow::hide)); |
851 |
|
|
852 |
|
// sample right-click popup actions |
853 |
|
actionGroup->add( |
854 |
|
Gtk::Action::create("SampleProperties", Gtk::Stock::PROPERTIES), |
855 |
|
sigc::mem_fun(*this, &MainWindow::on_action_sample_properties) |
856 |
|
); |
857 |
|
actionGroup->add( |
858 |
|
Gtk::Action::create("AddGroup", _("Add _Group")), |
859 |
|
sigc::mem_fun(*this, &MainWindow::on_action_add_group) |
860 |
|
); |
861 |
|
actionGroup->add( |
862 |
|
Gtk::Action::create("AddSample", _("Add _Sample(s)")), |
863 |
|
sigc::mem_fun(*this, &MainWindow::on_action_add_sample) |
864 |
|
); |
865 |
|
actionGroup->add( |
866 |
|
Gtk::Action::create("RemoveSample", Gtk::Stock::REMOVE), |
867 |
|
sigc::mem_fun(*this, &MainWindow::on_action_remove_sample) |
868 |
|
); |
869 |
|
|
870 |
uiManager = Gtk::UIManager::create(); |
uiManager = Gtk::UIManager::create(); |
871 |
uiManager->insert_action_group(actionGroup); |
uiManager->insert_action_group(actionGroup); |
872 |
// add_accel_group(uiManager->get_accel_group()); |
// add_accel_group(uiManager->get_accel_group()); |
885 |
" <separator/>" |
" <separator/>" |
886 |
" <menuitem action='Quit'/>" |
" <menuitem action='Quit'/>" |
887 |
" </menu>" |
" </menu>" |
888 |
|
" <menu action='MenuInstrument'>" |
889 |
|
" </menu>" |
890 |
#ifdef ABOUT_DIALOG |
#ifdef ABOUT_DIALOG |
891 |
" <menu action='MenuHelp'>" |
" <menu action='MenuHelp'>" |
892 |
" <menuitem action='About'/>" |
" <menuitem action='About'/>" |
897 |
" <menuitem action='InstrProperties'/>" |
" <menuitem action='InstrProperties'/>" |
898 |
" <menuitem action='Remove'/>" |
" <menuitem action='Remove'/>" |
899 |
" </popup>" |
" </popup>" |
900 |
|
" <popup name='SamplePopupMenu'>" |
901 |
|
" <menuitem action='SampleProperties'/>" |
902 |
|
" <menuitem action='AddGroup'/>" |
903 |
|
" <menuitem action='AddSample'/>" |
904 |
|
" <separator/>" |
905 |
|
" <menuitem action='RemoveSample'/>" |
906 |
|
" </popup>" |
907 |
"</ui>"; |
"</ui>"; |
908 |
uiManager->add_ui_from_string(ui_info); |
uiManager->add_ui_from_string(ui_info); |
909 |
|
|
924 |
// Create the Tree model: |
// Create the Tree model: |
925 |
m_refTreeModel = Gtk::ListStore::create(m_Columns); |
m_refTreeModel = Gtk::ListStore::create(m_Columns); |
926 |
m_TreeView.set_model(m_refTreeModel); |
m_TreeView.set_model(m_refTreeModel); |
927 |
|
m_refTreeModel->signal_row_changed().connect( |
928 |
|
sigc::mem_fun(*this, &MainWindow::instrument_name_changed) |
929 |
|
); |
930 |
|
|
931 |
// Add the TreeView's view columns: |
// Add the TreeView's view columns: |
932 |
m_TreeView.append_column("Instrument", m_Columns.m_col_name); |
m_TreeView.append_column_editable("Instrument", m_Columns.m_col_name); |
933 |
m_TreeView.set_headers_visible(false); |
m_TreeView.set_headers_visible(false); |
934 |
|
|
935 |
|
// create samples treeview (including its data model) |
936 |
|
m_refSamplesTreeModel = SamplesTreeStore::create(m_SamplesModel); |
937 |
|
m_TreeViewSamples.set_model(m_refSamplesTreeModel); |
938 |
|
m_TreeViewSamples.append_column_editable("Samples", m_SamplesModel.m_col_name); |
939 |
|
m_TreeViewSamples.set_headers_visible(false); |
940 |
|
m_TreeViewSamples.signal_button_press_event().connect_notify( |
941 |
|
sigc::mem_fun(*this, &MainWindow::on_sample_treeview_button_release) |
942 |
|
); |
943 |
|
m_refSamplesTreeModel->signal_row_changed().connect( |
944 |
|
sigc::mem_fun(*this, &MainWindow::sample_name_changed) |
945 |
|
); |
946 |
|
|
947 |
|
// establish drag&drop between samples tree view and dimension region 'Sample' text entry |
948 |
|
std::list<Gtk::TargetEntry> drag_target_gig_sample; |
949 |
|
drag_target_gig_sample.push_back( Gtk::TargetEntry("gig::Sample") ); |
950 |
|
//drag_target_gig_sample.push_back( Gtk::TargetEntry("STRING") ); |
951 |
|
//drag_target_gig_sample.push_back( Gtk::TargetEntry("text/plain") ); |
952 |
|
m_TreeViewSamples.drag_source_set(drag_target_gig_sample); |
953 |
|
m_TreeViewSamples.signal_drag_data_get().connect( |
954 |
|
sigc::mem_fun(*this, &MainWindow::on_sample_treeview_drag_data_get) |
955 |
|
); |
956 |
|
wSample->drag_dest_set(drag_target_gig_sample); |
957 |
|
wSample->signal_drag_data_received().connect( |
958 |
|
sigc::mem_fun(*this, &MainWindow::on_sample_label_drop_drag_data_received) |
959 |
|
); |
960 |
|
|
961 |
file = 0; |
file = 0; |
962 |
|
|
963 |
show_all_children(); |
show_all_children(); |
996 |
{ |
{ |
997 |
update_gui = false; |
update_gui = false; |
998 |
wSample->set_text(d->pSample ? d->pSample->pInfo->Name.c_str() : "NULL"); |
wSample->set_text(d->pSample ? d->pSample->pInfo->Name.c_str() : "NULL"); |
|
eVelocityUpperLimit.set_dimreg(d); |
|
999 |
eEG1PreAttack.set_dimreg(d); |
eEG1PreAttack.set_dimreg(d); |
1000 |
eEG1Attack.set_dimreg(d); |
eEG1Attack.set_dimreg(d); |
1001 |
eEG1Decay1.set_dimreg(d); |
eEG1Decay1.set_dimreg(d); |
1303 |
|
|
1304 |
void MainWindow::on_action_file_new() |
void MainWindow::on_action_file_new() |
1305 |
{ |
{ |
1306 |
|
m_SampleImportQueue.clear(); |
1307 |
} |
} |
1308 |
|
|
1309 |
void MainWindow::on_action_file_open() |
void MainWindow::on_action_file_open() |
1317 |
if (dialog.run() == Gtk::RESPONSE_OK) { |
if (dialog.run() == Gtk::RESPONSE_OK) { |
1318 |
printf("filename=%s\n", dialog.get_filename().c_str()); |
printf("filename=%s\n", dialog.get_filename().c_str()); |
1319 |
|
|
1320 |
|
// remove all entries from "Instrument" menu |
1321 |
|
Gtk::MenuItem* instrument_menu = |
1322 |
|
dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument")); |
1323 |
|
instrument_menu->hide(); |
1324 |
|
for (int i = 0; i < instrument_menu->get_submenu()->items().size(); i++) { |
1325 |
|
delete &instrument_menu->get_submenu()->items()[i]; |
1326 |
|
} |
1327 |
|
instrument_menu->get_submenu()->items().clear(); |
1328 |
|
|
1329 |
|
m_SampleImportQueue.clear(); |
1330 |
m_refTreeModel->clear(); |
m_refTreeModel->clear(); |
1331 |
|
m_refSamplesTreeModel->clear(); |
1332 |
if (file) delete file; |
if (file) delete file; |
1333 |
|
|
1334 |
// getInfo(dialog.get_filename().c_str(), *this); |
// getInfo(dialog.get_filename().c_str(), *this); |
1366 |
|
|
1367 |
void MainWindow::on_action_file_save() |
void MainWindow::on_action_file_save() |
1368 |
{ |
{ |
1369 |
|
if (!file) return; |
1370 |
|
std::cout << "Saving file\n" << std::flush; |
1371 |
|
try { |
1372 |
|
file->Save(); |
1373 |
|
} catch (RIFF::Exception e) { |
1374 |
|
Glib::ustring txt = "Could not save file: " + e.Message; |
1375 |
|
Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); |
1376 |
|
msg.run(); |
1377 |
|
return; |
1378 |
|
} |
1379 |
|
std::cout << "Saving file done\n" << std::flush; |
1380 |
|
__import_queued_samples(); |
1381 |
} |
} |
1382 |
|
|
1383 |
void MainWindow::on_action_file_save_as() |
void MainWindow::on_action_file_save_as() |
1384 |
{ |
{ |
1385 |
|
if (!file) return; |
1386 |
Gtk::FileChooserDialog dialog(*this, "Open", Gtk::FILE_CHOOSER_ACTION_SAVE); |
Gtk::FileChooserDialog dialog(*this, "Open", Gtk::FILE_CHOOSER_ACTION_SAVE); |
1387 |
dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); |
dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); |
1388 |
dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); |
dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); |
1391 |
dialog.set_filter(filter); |
dialog.set_filter(filter); |
1392 |
if (dialog.run() == Gtk::RESPONSE_OK) { |
if (dialog.run() == Gtk::RESPONSE_OK) { |
1393 |
printf("filename=%s\n", dialog.get_filename().c_str()); |
printf("filename=%s\n", dialog.get_filename().c_str()); |
1394 |
file->Save(dialog.get_filename()); |
try { |
1395 |
|
file->Save(dialog.get_filename()); |
1396 |
|
} catch (RIFF::Exception e) { |
1397 |
|
Glib::ustring txt = "Could not save file: " + e.Message; |
1398 |
|
Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); |
1399 |
|
msg.run(); |
1400 |
|
return; |
1401 |
|
} |
1402 |
|
__import_queued_samples(); |
1403 |
|
} |
1404 |
|
} |
1405 |
|
|
1406 |
|
// actually write the sample(s)' data to the gig file |
1407 |
|
void MainWindow::__import_queued_samples() { |
1408 |
|
std::cout << "Starting sample import\n" << std::flush; |
1409 |
|
Glib::ustring error_files; |
1410 |
|
printf("Samples to import: %d\n", m_SampleImportQueue.size()); |
1411 |
|
for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin(); iter != m_SampleImportQueue.end(); ) { |
1412 |
|
printf("Importing sample %s\n",(*iter).sample_path.c_str()); |
1413 |
|
SF_INFO info; |
1414 |
|
info.format = 0; |
1415 |
|
SNDFILE* hFile = sf_open((*iter).sample_path.c_str(), SFM_READ, &info); |
1416 |
|
try { |
1417 |
|
if (!hFile) throw std::string("could not open file"); |
1418 |
|
// determine sample's bit depth |
1419 |
|
int bitdepth; |
1420 |
|
switch (info.format & 0xff) { |
1421 |
|
case SF_FORMAT_PCM_S8: |
1422 |
|
bitdepth = 16; // we simply convert to 16 bit for now |
1423 |
|
break; |
1424 |
|
case SF_FORMAT_PCM_16: |
1425 |
|
bitdepth = 16; |
1426 |
|
break; |
1427 |
|
case SF_FORMAT_PCM_24: |
1428 |
|
bitdepth = 32; // we simply convert to 32 bit for now |
1429 |
|
break; |
1430 |
|
case SF_FORMAT_PCM_32: |
1431 |
|
bitdepth = 32; |
1432 |
|
break; |
1433 |
|
case SF_FORMAT_PCM_U8: |
1434 |
|
bitdepth = 16; // we simply convert to 16 bit for now |
1435 |
|
break; |
1436 |
|
case SF_FORMAT_FLOAT: |
1437 |
|
bitdepth = 32; |
1438 |
|
break; |
1439 |
|
case SF_FORMAT_DOUBLE: |
1440 |
|
bitdepth = 32; // I guess we will always truncate this to 32 bit |
1441 |
|
break; |
1442 |
|
default: |
1443 |
|
sf_close(hFile); // close sound file |
1444 |
|
throw std::string("format not supported"); // unsupported subformat (yet?) |
1445 |
|
} |
1446 |
|
// allocate appropriate copy buffer (TODO: for now we copy it in one piece, might be tough for very long samples) |
1447 |
|
// and copy sample data into buffer |
1448 |
|
int8_t* buffer = NULL; |
1449 |
|
switch (bitdepth) { |
1450 |
|
case 16: |
1451 |
|
buffer = new int8_t[2 * info.channels * info.frames]; |
1452 |
|
sf_readf_short(hFile, (short*) buffer, info.frames); // libsndfile does the conversion for us (if needed) |
1453 |
|
break; |
1454 |
|
case 32: |
1455 |
|
buffer = new int8_t[4 * info.channels * info.frames]; |
1456 |
|
sf_readf_int(hFile, (int*) buffer, info.frames); // libsndfile does the conversion for us (if needed) |
1457 |
|
break; |
1458 |
|
} |
1459 |
|
// write from buffer directly (physically) into .gig file |
1460 |
|
(*iter).gig_sample->Write(buffer, info.frames); |
1461 |
|
// cleanup |
1462 |
|
sf_close(hFile); |
1463 |
|
delete buffer; |
1464 |
|
// on success we remove the sample from the import queue, otherwise keep it, maybe it works the next time ? |
1465 |
|
std::list<SampleImportItem>::iterator cur = iter; |
1466 |
|
++iter; |
1467 |
|
m_SampleImportQueue.erase(cur); |
1468 |
|
} catch (std::string what) { // remember the files that made trouble (and their cause) |
1469 |
|
if (error_files.size()) error_files += "\n"; |
1470 |
|
error_files += (*iter).sample_path += " (" + what + ")"; |
1471 |
|
++iter; |
1472 |
|
} |
1473 |
|
} |
1474 |
|
// show error message box when some sample(s) could not be imported |
1475 |
|
if (error_files.size()) { |
1476 |
|
Glib::ustring txt = "Could not import the following sample(s):\n" + error_files; |
1477 |
|
Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); |
1478 |
|
msg.run(); |
1479 |
} |
} |
1480 |
} |
} |
1481 |
|
|
1657 |
|
|
1658 |
propDialog.set_info(gig->pInfo); |
propDialog.set_info(gig->pInfo); |
1659 |
|
|
1660 |
|
Gtk::MenuItem* instrument_menu = |
1661 |
|
dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/MenuBar/MenuInstrument")); |
1662 |
|
|
1663 |
|
int instrument_index = 0; |
1664 |
|
Gtk::RadioMenuItem::Group instrument_group; |
1665 |
for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ; |
for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ; |
1666 |
instrument = gig->GetNextInstrument()) { |
instrument = gig->GetNextInstrument()) { |
1667 |
Gtk::TreeModel::iterator iter = m_refTreeModel->append(); |
Gtk::TreeModel::iterator iter = m_refTreeModel->append(); |
1668 |
Gtk::TreeModel::Row row = *iter; |
Gtk::TreeModel::Row row = *iter; |
1669 |
row[m_Columns.m_col_name] = instrument->pInfo->Name.c_str(); |
row[m_Columns.m_col_name] = instrument->pInfo->Name.c_str(); |
1670 |
row[m_Columns.m_col_instr] = instrument; |
row[m_Columns.m_col_instr] = instrument; |
1671 |
|
// create a menu item for this instrument |
1672 |
|
Gtk::RadioMenuItem* item= new Gtk::RadioMenuItem(instrument_group, instrument->pInfo->Name.c_str()); |
1673 |
|
instrument_menu->get_submenu()->append(*item); |
1674 |
|
item->signal_activate().connect( |
1675 |
|
sigc::bind( |
1676 |
|
sigc::mem_fun(*this, &MainWindow::on_instrument_selection_change), |
1677 |
|
instrument_index |
1678 |
|
) |
1679 |
|
); |
1680 |
|
instrument_index++; |
1681 |
|
} |
1682 |
|
instrument_menu->show(); |
1683 |
|
instrument_menu->get_submenu()->show_all_children(); |
1684 |
|
|
1685 |
|
for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) { |
1686 |
|
if (group->Name != "") { |
1687 |
|
Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append(); |
1688 |
|
Gtk::TreeModel::Row rowGroup = *iterGroup; |
1689 |
|
rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str(); |
1690 |
|
rowGroup[m_SamplesModel.m_col_group] = group; |
1691 |
|
rowGroup[m_SamplesModel.m_col_sample] = NULL; |
1692 |
|
for (gig::Sample* sample = group->GetFirstSample(); sample; sample = group->GetNextSample()) { |
1693 |
|
Gtk::TreeModel::iterator iterSample = m_refSamplesTreeModel->append(rowGroup.children()); |
1694 |
|
Gtk::TreeModel::Row rowSample = *iterSample; |
1695 |
|
rowSample[m_SamplesModel.m_col_name] = sample->pInfo->Name.c_str(); |
1696 |
|
rowSample[m_SamplesModel.m_col_sample] = sample; |
1697 |
|
rowSample[m_SamplesModel.m_col_group] = NULL; |
1698 |
|
} |
1699 |
|
} |
1700 |
} |
} |
1701 |
} |
} |
1702 |
|
|
1719 |
popup_menu->popup(button->button, button->time); |
popup_menu->popup(button->button, button->time); |
1720 |
} |
} |
1721 |
} |
} |
1722 |
|
|
1723 |
|
void MainWindow::on_instrument_selection_change(int index) { |
1724 |
|
m_RegionChooser.set_instrument(file->GetInstrument(index)); |
1725 |
|
} |
1726 |
|
|
1727 |
|
void MainWindow::on_sample_treeview_button_release(GdkEventButton* button) { |
1728 |
|
if (button->type == GDK_BUTTON_PRESS && button->button == 3) { |
1729 |
|
Gtk::Menu* sample_popup = |
1730 |
|
dynamic_cast<Gtk::Menu*>(uiManager->get_widget("/SamplePopupMenu")); |
1731 |
|
// update enabled/disabled state of sample popup items |
1732 |
|
Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection(); |
1733 |
|
Gtk::TreeModel::iterator it = sel->get_selected(); |
1734 |
|
bool group_selected = false; |
1735 |
|
bool sample_selected = false; |
1736 |
|
if (it) { |
1737 |
|
Gtk::TreeModel::Row row = *it; |
1738 |
|
group_selected = row[m_SamplesModel.m_col_group]; |
1739 |
|
sample_selected = row[m_SamplesModel.m_col_sample]; |
1740 |
|
} |
1741 |
|
dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/SampleProperties"))->set_sensitive(group_selected || sample_selected); |
1742 |
|
dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddSample"))->set_sensitive(group_selected || sample_selected); |
1743 |
|
dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/AddGroup"))->set_sensitive(file); |
1744 |
|
dynamic_cast<Gtk::MenuItem*>(uiManager->get_widget("/SamplePopupMenu/RemoveSample"))->set_sensitive(group_selected || sample_selected); |
1745 |
|
// show sample popup |
1746 |
|
sample_popup->popup(button->button, button->time); |
1747 |
|
} |
1748 |
|
} |
1749 |
|
|
1750 |
|
void MainWindow::on_action_sample_properties() { |
1751 |
|
//TODO: show a dialog where the selected sample's properties can be edited |
1752 |
|
} |
1753 |
|
|
1754 |
|
void MainWindow::on_action_add_group() { |
1755 |
|
static int __sample_indexer = 0; |
1756 |
|
if (!file) return; |
1757 |
|
gig::Group* group = file->AddGroup(); |
1758 |
|
group->Name = "Unnamed Group"; |
1759 |
|
if (__sample_indexer) group->Name += " " + ToString(__sample_indexer); |
1760 |
|
__sample_indexer++; |
1761 |
|
// update sample tree view |
1762 |
|
Gtk::TreeModel::iterator iterGroup = m_refSamplesTreeModel->append(); |
1763 |
|
Gtk::TreeModel::Row rowGroup = *iterGroup; |
1764 |
|
rowGroup[m_SamplesModel.m_col_name] = group->Name.c_str(); |
1765 |
|
rowGroup[m_SamplesModel.m_col_sample] = NULL; |
1766 |
|
rowGroup[m_SamplesModel.m_col_group] = group; |
1767 |
|
} |
1768 |
|
|
1769 |
|
void MainWindow::on_action_add_sample() { |
1770 |
|
if (!file) return; |
1771 |
|
// get selected group |
1772 |
|
Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection(); |
1773 |
|
Gtk::TreeModel::iterator it = sel->get_selected(); |
1774 |
|
if (!it) return; |
1775 |
|
Gtk::TreeModel::Row row = *it; |
1776 |
|
gig::Group* group = row[m_SamplesModel.m_col_group]; |
1777 |
|
if (!group) { // not a group, but a sample is selected (probably) |
1778 |
|
gig::Sample* sample = row[m_SamplesModel.m_col_sample]; |
1779 |
|
if (!sample) return; |
1780 |
|
it = row.parent(); // resolve parent (that is the sample's group) |
1781 |
|
if (!it) return; |
1782 |
|
row = *it; |
1783 |
|
group = row[m_SamplesModel.m_col_group]; |
1784 |
|
if (!group) return; |
1785 |
|
} |
1786 |
|
// show 'browse for file' dialog |
1787 |
|
Gtk::FileChooserDialog dialog(*this, _("Add Sample(s)")); |
1788 |
|
dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); |
1789 |
|
dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); |
1790 |
|
dialog.set_select_multiple(true); |
1791 |
|
Gtk::FileFilter soundfilter; // matches all file types supported by libsndfile |
1792 |
|
const char* supportedFileTypes[] = { |
1793 |
|
"*.wav", "*.WAV", "*.aiff", "*.AIFF", "*.aifc", "*.AIFC", "*.snd", |
1794 |
|
"*.SND", "*.au", "*.AU", "*.paf", "*.PAF", "*.iff", "*.IFF", |
1795 |
|
"*.svx", "*.SVX", "*.sf", "*.SF", "*.voc", "*.VOC", "*.w64", |
1796 |
|
"*.W64", "*.pvf", "*.PVF", "*.xi", "*.XI", "*.htk", "*.HTK", |
1797 |
|
"*.caf", "*.CAF", NULL |
1798 |
|
}; |
1799 |
|
for (int i = 0; supportedFileTypes[i]; i++) |
1800 |
|
soundfilter.add_pattern(supportedFileTypes[i]); |
1801 |
|
soundfilter.set_name("Sound Files"); |
1802 |
|
Gtk::FileFilter allpassfilter; // matches every file |
1803 |
|
allpassfilter.add_pattern("*.*"); |
1804 |
|
allpassfilter.set_name("All Files"); |
1805 |
|
dialog.add_filter(soundfilter); |
1806 |
|
dialog.add_filter(allpassfilter); |
1807 |
|
if (dialog.run() == Gtk::RESPONSE_OK) { |
1808 |
|
Glib::ustring error_files; |
1809 |
|
Glib::SListHandle<Glib::ustring> filenames = dialog.get_filenames(); |
1810 |
|
for (Glib::SListHandle<Glib::ustring>::iterator iter = filenames.begin(); iter != filenames.end(); ++iter) { |
1811 |
|
printf("Adding sample %s\n",(*iter).c_str()); |
1812 |
|
// use libsndfile to retrieve file informations |
1813 |
|
SF_INFO info; |
1814 |
|
info.format = 0; |
1815 |
|
SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info); |
1816 |
|
try { |
1817 |
|
if (!hFile) throw std::string("could not open file"); |
1818 |
|
int bitdepth; |
1819 |
|
switch (info.format & 0xff) { |
1820 |
|
case SF_FORMAT_PCM_S8: |
1821 |
|
bitdepth = 16; // we simply convert to 16 bit for now |
1822 |
|
break; |
1823 |
|
case SF_FORMAT_PCM_16: |
1824 |
|
bitdepth = 16; |
1825 |
|
break; |
1826 |
|
case SF_FORMAT_PCM_24: |
1827 |
|
bitdepth = 32; // we simply convert to 32 bit for now |
1828 |
|
break; |
1829 |
|
case SF_FORMAT_PCM_32: |
1830 |
|
bitdepth = 32; |
1831 |
|
break; |
1832 |
|
case SF_FORMAT_PCM_U8: |
1833 |
|
bitdepth = 16; // we simply convert to 16 bit for now |
1834 |
|
break; |
1835 |
|
case SF_FORMAT_FLOAT: |
1836 |
|
bitdepth = 32; |
1837 |
|
break; |
1838 |
|
case SF_FORMAT_DOUBLE: |
1839 |
|
bitdepth = 32; // I guess we will always truncate this to 32 bit |
1840 |
|
break; |
1841 |
|
default: |
1842 |
|
sf_close(hFile); // close sound file |
1843 |
|
throw std::string("format not supported"); // unsupported subformat (yet?) |
1844 |
|
} |
1845 |
|
// add a new sample to the .gig file |
1846 |
|
gig::Sample* sample = file->AddSample(); |
1847 |
|
sample->pInfo->Name = (*iter).substr((*iter).rfind('/') + 1).raw(); // file name without path |
1848 |
|
sample->Channels = info.channels; |
1849 |
|
sample->BitDepth = bitdepth; |
1850 |
|
sample->FrameSize = bitdepth / 8/*1 byte are 8 bits*/ * info.channels; |
1851 |
|
sample->SamplesPerSecond = info.samplerate; |
1852 |
|
// schedule resizing the sample (which will be done physically when File::Save() is called) |
1853 |
|
sample->Resize(info.frames); |
1854 |
|
// make sure sample is part of the selected group |
1855 |
|
group->AddSample(sample); |
1856 |
|
// schedule that physical resize and sample import (data copying), performed when "Save" is requested |
1857 |
|
SampleImportItem sched_item; |
1858 |
|
sched_item.gig_sample = sample; |
1859 |
|
sched_item.sample_path = *iter; |
1860 |
|
m_SampleImportQueue.push_back(sched_item); |
1861 |
|
// add sample to the tree view |
1862 |
|
Gtk::TreeModel::iterator iterSample = m_refSamplesTreeModel->append(row.children()); |
1863 |
|
Gtk::TreeModel::Row rowSample = *iterSample; |
1864 |
|
rowSample[m_SamplesModel.m_col_name] = sample->pInfo->Name.c_str(); |
1865 |
|
rowSample[m_SamplesModel.m_col_sample] = sample; |
1866 |
|
rowSample[m_SamplesModel.m_col_group] = NULL; |
1867 |
|
// close sound file |
1868 |
|
sf_close(hFile); |
1869 |
|
} catch (std::string what) { // remember the files that made trouble (and their cause) |
1870 |
|
if (error_files.size()) error_files += "\n"; |
1871 |
|
error_files += *iter += " (" + what + ")"; |
1872 |
|
} |
1873 |
|
} |
1874 |
|
// show error message box when some file(s) could not be opened / added |
1875 |
|
if (error_files.size()) { |
1876 |
|
Glib::ustring txt = "Could not add the following sample(s):\n" + error_files; |
1877 |
|
Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); |
1878 |
|
msg.run(); |
1879 |
|
} |
1880 |
|
} |
1881 |
|
} |
1882 |
|
|
1883 |
|
void MainWindow::on_action_remove_sample() { |
1884 |
|
if (!file) return; |
1885 |
|
Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection(); |
1886 |
|
Gtk::TreeModel::iterator it = sel->get_selected(); |
1887 |
|
if (it) { |
1888 |
|
Gtk::TreeModel::Row row = *it; |
1889 |
|
gig::Group* group = row[m_SamplesModel.m_col_group]; |
1890 |
|
gig::Sample* sample = row[m_SamplesModel.m_col_sample]; |
1891 |
|
Glib::ustring name = row[m_SamplesModel.m_col_name]; |
1892 |
|
try { |
1893 |
|
// remove group or sample from the gig file |
1894 |
|
if (group) { |
1895 |
|
// temporarily remember the samples that bolong to that group (we need that to clean the queue) |
1896 |
|
std::list<gig::Sample*> members; |
1897 |
|
for (gig::Sample* pSample = group->GetFirstSample(); pSample; pSample = group->GetNextSample()) { |
1898 |
|
members.push_back(pSample); |
1899 |
|
} |
1900 |
|
// delete the group in the .gig file including the samples that belong to the group |
1901 |
|
file->DeleteGroup(group); |
1902 |
|
// if sample(s) were just previously added, remove them from the import queue |
1903 |
|
for (std::list<gig::Sample*>::iterator member = members.begin(); member != members.end(); ++member) { |
1904 |
|
for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin(); iter != m_SampleImportQueue.end(); ++iter) { |
1905 |
|
if ((*iter).gig_sample == *member) { |
1906 |
|
printf("Removing previously added sample '%s' from group '%s'\n", (*iter).sample_path.c_str(), name.c_str()); |
1907 |
|
m_SampleImportQueue.erase(iter); |
1908 |
|
break; |
1909 |
|
} |
1910 |
|
} |
1911 |
|
} |
1912 |
|
} else if (sample) { |
1913 |
|
// remove sample from the .gig file |
1914 |
|
file->DeleteSample(sample); |
1915 |
|
// if sample was just previously added, remove it from the import queue |
1916 |
|
for (std::list<SampleImportItem>::iterator iter = m_SampleImportQueue.begin(); iter != m_SampleImportQueue.end(); ++iter) { |
1917 |
|
if ((*iter).gig_sample == sample) { |
1918 |
|
printf("Removing previously added sample '%s'\n", (*iter).sample_path.c_str()); |
1919 |
|
m_SampleImportQueue.erase(iter); |
1920 |
|
break; |
1921 |
|
} |
1922 |
|
} |
1923 |
|
} |
1924 |
|
// remove respective row(s) from samples tree view |
1925 |
|
m_refSamplesTreeModel->erase(it); |
1926 |
|
} catch (RIFF::Exception e) { |
1927 |
|
Gtk::MessageDialog msg(*this, e.Message.c_str(), false, Gtk::MESSAGE_ERROR); |
1928 |
|
msg.run(); |
1929 |
|
} |
1930 |
|
} |
1931 |
|
} |
1932 |
|
|
1933 |
|
void MainWindow::on_sample_treeview_drag_data_get(const Glib::RefPtr<Gdk::DragContext>&, Gtk::SelectionData& selection_data, guint, guint) |
1934 |
|
{ |
1935 |
|
// get selected sample |
1936 |
|
gig::Sample* sample = NULL; |
1937 |
|
Glib::RefPtr<Gtk::TreeSelection> sel = m_TreeViewSamples.get_selection(); |
1938 |
|
Gtk::TreeModel::iterator it = sel->get_selected(); |
1939 |
|
if (it) { |
1940 |
|
Gtk::TreeModel::Row row = *it; |
1941 |
|
sample = row[m_SamplesModel.m_col_sample]; |
1942 |
|
} |
1943 |
|
// pass the gig::Sample as pointer |
1944 |
|
selection_data.set(selection_data.get_target(), 0/*unused*/, (const guchar*)&sample, sizeof(sample)/*length of data in bytes*/); |
1945 |
|
} |
1946 |
|
|
1947 |
|
void MainWindow::on_sample_label_drop_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int, int, const Gtk::SelectionData& selection_data, guint, guint time) |
1948 |
|
{ |
1949 |
|
gig::DimensionRegion* dimregion = m_DimRegionChooser.get_dimregion(); |
1950 |
|
gig::Sample* sample = *((gig::Sample**) selection_data.get_data()); |
1951 |
|
|
1952 |
|
if (sample && dimregion && selection_data.get_length() == sizeof(gig::Sample*)) { |
1953 |
|
if (sample != dimregion->pSample) { |
1954 |
|
dimregion->pSample = sample; |
1955 |
|
wSample->set_text(dimregion->pSample->pInfo->Name.c_str()); |
1956 |
|
std::cout << "Drop received sample \"" << dimregion->pSample->pInfo->Name.c_str() << "\"" << std::endl; |
1957 |
|
// drop success |
1958 |
|
context->drop_reply(true, time); |
1959 |
|
return; |
1960 |
|
} |
1961 |
|
} |
1962 |
|
// drop failed |
1963 |
|
context->drop_reply(false, time); |
1964 |
|
} |
1965 |
|
|
1966 |
|
void MainWindow::sample_name_changed(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter) { |
1967 |
|
if (!iter) return; |
1968 |
|
Gtk::TreeModel::Row row = *iter; |
1969 |
|
Glib::ustring name = row[m_SamplesModel.m_col_name]; |
1970 |
|
gig::Group* group = row[m_SamplesModel.m_col_group]; |
1971 |
|
gig::Sample* sample = row[m_SamplesModel.m_col_sample]; |
1972 |
|
if (group) { |
1973 |
|
group->Name = name; |
1974 |
|
std::cout << "Group name changed\n" << std::flush; |
1975 |
|
} else if (sample) { |
1976 |
|
sample->pInfo->Name = name.raw(); |
1977 |
|
std::cout << "Sample name changed\n" << std::flush; |
1978 |
|
} |
1979 |
|
} |
1980 |
|
|
1981 |
|
void MainWindow::instrument_name_changed(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter) { |
1982 |
|
std::cout << "Instrument name changed\n" << std::flush; |
1983 |
|
if (!iter) return; |
1984 |
|
Gtk::TreeModel::Row row = *iter; |
1985 |
|
Glib::ustring name = row[m_Columns.m_col_name]; |
1986 |
|
gig::Instrument* instrument = row[m_Columns.m_col_instr]; |
1987 |
|
if (instrument) instrument->pInfo->Name = name.raw(); |
1988 |
|
} |