--- gigedit/trunk/src/mainwindow.cpp 2007/03/08 01:43:18 1082 +++ gigedit/trunk/src/mainwindow.cpp 2007/03/10 08:16:38 1088 @@ -27,8 +27,12 @@ #if GTKMM_MAJOR_VERSION == 2 && GTKMM_MINOR_VERSION >= 6 #define ABOUT_DIALOG #include +#include #endif +#include +#include + #define _(String) gettext(String) template inline std::string ToString(T o) { @@ -516,7 +520,7 @@ eSampleLoops("SampleLoops", &access_SampleLoops, 0, 1) { // set_border_width(5); - set_default_size(400, 200); +// set_default_size(400, 200); add(m_VBox); @@ -530,10 +534,14 @@ sigc::mem_fun(*this, &MainWindow::on_button_release)); // Add the TreeView tab, inside a ScrolledWindow, with the button underneath: - m_ScrolledWindow.add(m_TreeViewNotebook); - m_ScrolledWindow.set_size_request(400, 600); + m_ScrolledWindow.add(m_TreeView); +// m_ScrolledWindow.set_size_request(200, 600); m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + m_ScrolledWindowSamples.add(m_TreeViewSamples); + m_ScrolledWindowSamples.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); @@ -783,14 +791,15 @@ 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_Notebook.set_size_request(400, 500); - m_HPaned.add1(m_ScrolledWindow); + m_TreeViewNotebook.set_size_request(300); + m_HPaned.add1(m_TreeViewNotebook); m_HPaned.add2(m_Notebook); - m_TreeViewNotebook.append_page(m_TreeViewSamples, "Samples"); - m_TreeViewNotebook.append_page(m_TreeView, "Instruments"); + m_TreeViewNotebook.append_page(m_ScrolledWindowSamples, "Samples"); + m_TreeViewNotebook.append_page(m_ScrolledWindow, "Instruments"); actionGroup = Gtk::ActionGroup::create(); @@ -851,7 +860,7 @@ sigc::mem_fun(*this, &MainWindow::on_action_add_group) ); actionGroup->add( - Gtk::Action::create("AddSample", _("Add _Sample")), + Gtk::Action::create("AddSample", _("Add _Sample(s)")), sigc::mem_fun(*this, &MainWindow::on_action_add_sample) ); actionGroup->add( @@ -1276,6 +1285,7 @@ void MainWindow::on_action_file_new() { + m_SampleImportQueue.clear(); } void MainWindow::on_action_file_open() @@ -1298,6 +1308,7 @@ } instrument_menu->get_submenu()->items().clear(); + m_SampleImportQueue.clear(); m_refTreeModel->clear(); m_refSamplesTreeModel->clear(); if (file) delete file; @@ -1337,10 +1348,14 @@ void MainWindow::on_action_file_save() { + if (!file) return; + file->Save(); + __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); @@ -1350,6 +1365,78 @@ if (dialog.run() == Gtk::RESPONSE_OK) { printf("filename=%s\n", dialog.get_filename().c_str()); file->Save(dialog.get_filename()); + __import_queued_samples(); + } +} + +// actually write the sample(s)' data to the gig file +void MainWindow::__import_queued_samples() { + Glib::ustring error_files; + for (std::list::iterator iter = m_SampleImportQueue.begin(); iter != m_SampleImportQueue.end(); ++iter) { + 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]; + sf_readf_short(hFile, (short*) buffer, info.frames); // libsndfile does the conversion for us (if needed) + break; + case 32: + buffer = new int8_t[4 * info.channels * info.frames]; + sf_readf_int(hFile, (int*) buffer, info.frames); // libsndfile does the conversion for us (if needed) + 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 ? + m_SampleImportQueue.erase(iter); + } 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 + ")"; + } + } + // 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(); } } @@ -1557,17 +1644,19 @@ instrument_menu->get_submenu()->show_all_children(); for (gig::Group* group = gig->GetFirstGroup(); group; group = gig->GetNextGroup()) { - 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; + 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; + } } } } @@ -1639,9 +1728,138 @@ } void MainWindow::on_action_add_sample() { - //TODO: open browse for file dialog for adding new samples + 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 (yet to do ;-) + soundfilter.add_pattern("*.wav"); + 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(); + sample->pInfo->Name = (*iter).substr((*iter).rfind('/') + 1).raw(); // file name without path + 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); + // 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() { - //TODO: remove the selected group or 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]; + try { + // remove group or sample from the gig file + if (group) { + file->DeleteGroup(group); + } else if (sample) { + file->DeleteSample(sample); + } + // if sample was just previously added, remove it from the import queue + if (sample) { + for (std::list::iterator iter = m_SampleImportQueue.begin(); iter != m_SampleImportQueue.end(); ++iter) { + if ((*iter).gig_sample == sample) { + 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(); + } + } }