/* SampleHive
* Copyright (C) 2021 Apoorv Singh
* A simple, modern audio sample browser/manager for GNU/Linux.
*
* This file is a part of SampleHive
*
* SampleHive is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SampleHive is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include "Database/Database.hpp"
#include "Utility/HiveData.hpp"
#include "Utility/Log.hpp"
#include "Utility/Paths.hpp"
#include "Utility/Serialize.hpp"
#include "Utility/Signal.hpp"
#include "Utility/Tags.hpp"
#include "Utility/Utils.hpp"
#include
#include
#include
#include
#include
namespace SampleHive {
SampleHive::cUtils::FileInfo SampleHive::cUtils::GetFilenamePathAndExtension(const wxString& selected,
bool checkExtension, bool doGetFilename)
{
wxString path;
std::string extension, filename;
path = GetSamplePath(selected);
if (checkExtension)
extension = path.AfterLast('/').AfterLast('.').ToStdString();
if (doGetFilename)
filename = path.AfterLast('/').BeforeLast('.').ToStdString();
return { path, extension, filename };
}
void SampleHive::cUtils::AddSamples(wxArrayString& files, wxWindow* parent)
{
SampleHive::cSerializer serializer;
cDatabase db;
wxBusyCursor busy_cursor;
wxWindowDisabler window_disabler;
wxProgressDialog* progressDialog = new wxProgressDialog(_("Adding files.."),
_("Adding files, please wait..."),
static_cast(files.size()), parent,
wxPD_APP_MODAL | wxPD_SMOOTH | wxPD_CAN_ABORT |
wxPD_AUTO_HIDE);
progressDialog->CenterOnParent(wxBOTH);
std::vector sample_array;
std::string path;
std::string artist;
std::string filename_with_extension;
std::string filename_without_extension;
std::string extension;
std::string filename;
float bpm = 0.0f;
float key = 0.0f;
//Check All Files At Once
wxArrayString sorted_files;
if (!serializer.DeserializeDemoMode())
{
sorted_files = db.CheckDuplicates(files);
files = sorted_files;
}
if (files.size() < 1)
{
progressDialog->Destroy();
return;
}
progressDialog->SetRange(files.size());
for (unsigned int i = 0; i < files.size(); i++)
{
progressDialog->Update(i, wxString::Format(_("Getting Data For %s"), files[i].AfterLast('/')));
if (progressDialog->WasCancelled())
{
progressDialog->Destroy();
return;
}
path = files[i].ToStdString();
filename_with_extension = files[i].AfterLast('/').ToStdString();
filename_without_extension = files[i].AfterLast('/').BeforeLast('.').ToStdString();
extension = files[i].AfterLast('.').ToStdString();
filename = serializer.DeserializeShowFileExtension() ?
filename_with_extension : filename_without_extension;
bpm = GetBPM(path);
Sample sample;
sample.SetPath(path);
sample.SetFilename(filename_without_extension);
sample.SetFileExtension(extension);
cTags tags(path);
artist = tags.GetAudioInfo().artist.ToStdString();
sample.SetSamplePack(artist);
sample.SetChannels(tags.GetAudioInfo().channels);
sample.SetBPM(static_cast(bpm));
sample.SetLength(tags.GetAudioInfo().length);
sample.SetSampleRate(tags.GetAudioInfo().sample_rate);
sample.SetBitrate(tags.GetAudioInfo().bitrate);
wxString length = CalculateAndGetISOStandardTime(sample.GetLength());
wxString bpm_str = GetBPMString(sample.GetBPM());
wxVector data;
wxVariant icon = wxVariant(wxBitmap(ICON_STAR_EMPTY_16px, wxBITMAP_TYPE_PNG));
if (tags.IsFileValid())
{
data.clear();
data.push_back(icon);
data.push_back(filename);
data.push_back(sample.GetSamplePack());
data.push_back("");
data.push_back(wxString::Format("%d", sample.GetChannels()));
data.push_back(bpm_str);
data.push_back(length);
data.push_back(wxString::Format("%d", sample.GetSampleRate()));
data.push_back(wxString::Format("%d", sample.GetBitrate()));
data.push_back(path);
SH_LOG_INFO("Adding file: {}, Extension: {}", sample.GetFilename(), sample.GetFileExtension());
SampleHive::cHiveData::Get().ListCtrlAppendItem(data);
sample_array.push_back(sample);
}
else
{
wxString msg = wxString::Format(_("Error! Cannot open %s, Invalid file type."),
filename_with_extension);
SampleHive::cSignal::SendInfoBarMessage(msg, wxICON_ERROR, *parent);
}
}
progressDialog->Pulse(_("Updating Database.."), NULL);
db.InsertIntoSamples(sample_array);
progressDialog->Destroy();
}
void cUtils::OnAutoImportDir(const wxString& pathToDirectory, wxWindow* parent)
{
SH_LOG_DEBUG("Start Importing Samples");
wxBusyCursor busy_cursor;
wxWindowDisabler window_disabler;
wxString filepath;
wxArrayString filepath_array;
size_t number_of_files = wxDir::GetAllFiles(pathToDirectory, &filepath_array, wxEmptyString, wxDIR_DEFAULT);
wxProgressDialog* progressDialog = new wxProgressDialog(_("Adding files.."),
_("Adding files, please wait..."),
static_cast(number_of_files), parent,
wxPD_APP_MODAL | wxPD_SMOOTH |
wxPD_CAN_ABORT | wxPD_AUTO_HIDE);
progressDialog->CenterOnParent(wxBOTH);
for (size_t i = 0; i < number_of_files; i++)
{
filepath = filepath_array[i];
if (wxFileExists(filepath))
{
filepath_array.push_back(filepath);
}
else if (wxDirExists(filepath))
{
wxDir::GetAllFiles(filepath, &filepath_array);
}
progressDialog->Pulse(_("Reading Samples"), NULL);
}
progressDialog->Destroy();
AddSamples(filepath_array, parent);
SH_LOG_DEBUG("Done Importing Samples");
}
std::string cUtils::GetSamplePath(const wxString& name)
{
SampleHive::cSerializer serializer;
cDatabase db;
wxString sample_path;
if (m_PathCache.find(name.ToStdString()) != m_PathCache.end())
return m_PathCache[name.ToStdString()];
else
{
sample_path = serializer.DeserializeShowFileExtension() ?
db.GetSamplePathByFilename(name.BeforeLast('.').ToStdString()) :
db.GetSamplePathByFilename(name.ToStdString());
m_PathCache[name.ToStdString()] = sample_path;
}
return sample_path.ToStdString();
}
wxString cUtils::CalculateAndGetISOStandardTime(wxLongLong length)
{
const int min_digits = 2;
const size_t max_digits = 2;
wxString iso_length;
int min = static_cast((length / 60000).GetValue());
int sec = static_cast(((length % 60000) / 1000).GetValue());
int ms = static_cast((length % 1000).GetValue());
iso_length = wxString::Format("%s:%s.%s",
wxString::Format("%0*i", min_digits, min).Left(max_digits),
wxString::Format("%0*i", min_digits, sec).Left(max_digits),
wxString::Format("%0*i", min_digits + 1, ms).Left(max_digits + 1));
return iso_length;
}
wxString cUtils::GetBPMString(float bpm)
{
return wxString::Format("%d", static_cast(bpm));
}
float cUtils::GetBPM(const std::string& path)
{
uint_t buff_size = 1024, hop_size = buff_size / 2, frames = 0, samplerate = 0, read = 0;
aubio_tempo_t* tempo;
fvec_t* in, *out;
aubio_source_t* source = new_aubio_source(path.c_str(), samplerate, hop_size);
float bpm = 0.0f;
if (!source)
return 0.0f;
else
{
try
{
if (samplerate == 0)
samplerate = aubio_source_get_samplerate(source);
tempo = new_aubio_tempo("default", buff_size, hop_size, samplerate);
if (!tempo)
return 0.0f;
in = new_fvec(hop_size);
out = new_fvec(1);
do
{
// put some fresh data in input vector
aubio_source_do(source, in, &read);
// execute tempo
aubio_tempo_do(tempo, in, out);
// do something with the beats
// if (out->data[0] != 0)
// {
// SH_LOG_DEBUG("Track: {} Beat at {}s, {}s, frame {}, {} bpm with confidence {}",
// path, aubio_tempo_get_last_ms(tempo), aubio_tempo_get_last_s(tempo),
// aubio_tempo_get_last(tempo), aubio_tempo_get_bpm(tempo),
// aubio_tempo_get_confidence(tempo));
// }
frames += read;
bpm = aubio_tempo_get_bpm(tempo);
}
while (read == hop_size);
// clean up memory
del_aubio_tempo(tempo);
del_fvec(in);
del_fvec(out);
del_aubio_source(source);
aubio_cleanup();
}
catch (std::exception& e)
{
SH_LOG_ERROR("Aubio Error! {}", e.what());
}
}
return bpm;
}
}