More consistent mutual exclusion in calls to certain sf_ functions; ...

... also SFCall to simplify thread-safe calls, and SFFile for RAII of SNDFILE
objects.
This commit is contained in:
Paul Licameli 2016-04-12 15:59:17 -04:00
parent 24df87bb4c
commit 61177a15ad
11 changed files with 360 additions and 384 deletions

View File

@ -285,4 +285,20 @@ OSType sf_header_mactype(int format)
return '\?\?\?\?';
}
#include <wx/msgdlg.h>
ODLock libSndFileMutex;
void SFFileCloser::operator() (SNDFILE *sf) const
{
auto err = SFCall<int>(sf_close, sf);
if (err) {
char buffer[1000];
sf_error_str(sf, buffer, 1000);
wxMessageBox(wxString::Format
/* i18n-hint: %s will be the error message from libsndfile */
(_("Error (file may not have been written): %s"),
buffer));
}
}
#endif // __WXMAC__

View File

@ -8,6 +8,9 @@
**********************************************************************/
#ifndef __AUDACITY_FILE_FORMATS__
#define __AUDACITY_FILE_FORMATS__
#include <wx/list.h>
#include <wx/arrstr.h>
#include <wx/string.h>
@ -108,6 +111,25 @@ wxString sf_normalize_name(const char *name);
# else
# include <Types.h>
# endif
#endif
OSType sf_header_mactype(int format);
// This function wrapper uses a mutex to serialize calls to the SndFile library.
#include "../MemoryX.h"
#include "../ondemand/ODTaskThread.h"
class ODLock;
class ODLocker;
extern ODLock libSndFileMutex;
template<typename R, typename F, typename... Args>
inline R SFCall(F fun, Args&&... args)
{
ODLocker locker{ libSndFileMutex };
return fun(std::forward<Args>(args)...);
}
//RAII for SNDFILE*
struct SFFileCloser { void operator () (SNDFILE*) const; };
using SFFile = std::unique_ptr<SNDFILE, SFFileCloser>;
#endif

View File

@ -212,13 +212,13 @@ int LegacyBlockFile::ReadData(samplePtr data, sampleFormat format,
SAMPLE_SIZE(mFormat));
wxFile f; // will be closed when it goes out of scope
SNDFILE *sf = NULL;
SFFile sf;
if (f.Open(mFileName.GetFullPath())) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE);
sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, FALSE));
}
{
@ -239,7 +239,7 @@ int LegacyBlockFile::ReadData(samplePtr data, sampleFormat format,
sf_count_t seekstart = start +
(mSummaryInfo.totalSummaryBytes / SAMPLE_SIZE(mFormat));
sf_seek(sf, seekstart , SEEK_SET);
SFCall<sf_count_t>(sf_seek, sf.get(), seekstart , SEEK_SET);
SampleBuffer buffer(len, floatSample);
int framesRead = 0;
@ -249,10 +249,11 @@ int LegacyBlockFile::ReadData(samplePtr data, sampleFormat format,
// converting to float and back, which is unneccesary)
if (format == int16Sample &&
sf_subtype_is_integer(info.format)) {
framesRead = sf_readf_short(sf, (short *)data, len);
}else if (format == int24Sample &&
framesRead = SFCall<sf_count_t>(sf_readf_short, sf.get(), (short *)data, len);
}
else if (format == int24Sample &&
sf_subtype_is_integer(info.format)) {
framesRead = sf_readf_int(sf, (int *)data, len);
framesRead = SFCall<sf_count_t>(sf_readf_int, sf.get(), (int *)data, len);
// libsndfile gave us the 3 byte sample in the 3 most
// significant bytes -- we want it in the 3 least
@ -264,13 +265,11 @@ int LegacyBlockFile::ReadData(samplePtr data, sampleFormat format,
// Otherwise, let libsndfile handle the conversion and
// scaling, and pass us normalized data as floats. We can
// then convert to whatever format we want.
framesRead = sf_readf_float(sf, (float *)buffer.ptr(), len);
framesRead = SFCall<sf_count_t>(sf_readf_float, sf.get(), (float *)buffer.ptr(), len);
CopySamples(buffer.ptr(), floatSample,
(samplePtr)data, format, framesRead);
}
sf_close(sf);
return framesRead;
}

View File

@ -606,34 +606,28 @@ int ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
if(!mAliasedFileName.IsOk()){ // intentionally silenced
memset(data,0,SAMPLE_SIZE(format)*len);
UnlockRead();
return len;
return len;
}
memset(&info, 0, sizeof(info));
wxString aliasPath = mAliasedFileName.GetFullPath();
//there are thread-unsafe crashes here - not sure why. sf_open may be called on the same file
//from different threads, but this seems okay, unless it is implemented strangely..
static ODLock sfMutex;
wxFile f; // will be closed when it goes out of scope
SNDFILE *sf = NULL;
SFFile sf;
if (f.Exists(aliasPath) && f.Open(aliasPath)) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
ODManager::LockLibSndFileMutex();
sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE);
ODManager::UnlockLibSndFileMutex();
sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, FALSE));
}
if (!sf){
if (!sf) {
memset(data,0,SAMPLE_SIZE(format)*len);
mSilentAliasLog=TRUE;
mSilentAliasLog = TRUE;
// Set a marker to display an error message
if (!wxGetApp().ShouldShowMissingAliasedFileWarning())
wxGetApp().MarkAliasedFilesMissingWarning(this);
@ -644,9 +638,7 @@ int ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
mSilentAliasLog=FALSE;
ODManager::LockLibSndFileMutex();
sf_seek(sf, mAliasStart + start, SEEK_SET);
ODManager::UnlockLibSndFileMutex();
SFCall<sf_count_t>(sf_seek, sf.get(), mAliasStart + start, SEEK_SET);
SampleBuffer buffer(len * info.channels, floatSample);
@ -658,9 +650,7 @@ int ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
// and the calling method wants 16-bit data, go ahead and
// read 16-bit data directly. This is a pretty common
// case, as most audio files are 16-bit.
ODManager::LockLibSndFileMutex();
framesRead = sf_readf_short(sf, (short *)buffer.ptr(), len);
ODManager::UnlockLibSndFileMutex();
framesRead = SFCall<sf_count_t>(sf_readf_short, sf.get(), (short *)buffer.ptr(), len);
for (int i = 0; i < framesRead; i++)
((short *)data)[i] =
@ -670,19 +660,13 @@ int ODPCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
// Otherwise, let libsndfile handle the conversion and
// scaling, and pass us normalized data as floats. We can
// then convert to whatever format we want.
ODManager::LockLibSndFileMutex();
framesRead = sf_readf_float(sf, (float *)buffer.ptr(), len);
ODManager::UnlockLibSndFileMutex();
framesRead = SFCall<sf_count_t>(sf_readf_float, sf.get(), (float *)buffer.ptr(), len);
float *bufferPtr = &((float *)buffer.ptr())[mAliasChannel];
CopySamples((samplePtr)bufferPtr, floatSample,
(samplePtr)data, format,
framesRead, true, info.channels);
}
ODManager::LockLibSndFileMutex();
sf_close(sf);
ODManager::UnlockLibSndFileMutex();
UnlockRead();
return framesRead;
}

View File

@ -84,7 +84,7 @@ int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
}
wxFile f; // will be closed when it goes out of scope
SNDFILE *sf = NULL;
SFFile sf;
{
Maybe<wxLogNull> silence{};
if (mSilentAliasLog)
@ -97,13 +97,11 @@ int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
ODManager::LockLibSndFileMutex();
sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE);
ODManager::UnlockLibSndFileMutex();
sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, FALSE));
}
}
if (!sf){
if (!sf) {
memset(data, 0, SAMPLE_SIZE(format)*len);
silence.reset();
mSilentAliasLog = TRUE;
@ -116,9 +114,7 @@ int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
}
mSilentAliasLog=FALSE;
ODManager::LockLibSndFileMutex();
sf_seek(sf, mAliasStart + start, SEEK_SET);
ODManager::UnlockLibSndFileMutex();
SFCall<sf_count_t>(sf_seek, sf.get(), mAliasStart + start, SEEK_SET);
SampleBuffer buffer(len * info.channels, floatSample);
int framesRead = 0;
@ -129,9 +125,7 @@ int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
// and the calling method wants 16-bit data, go ahead and
// read 16-bit data directly. This is a pretty common
// case, as most audio files are 16-bit.
ODManager::LockLibSndFileMutex();
framesRead = sf_readf_short(sf, (short *)buffer.ptr(), len);
ODManager::UnlockLibSndFileMutex();
framesRead = SFCall<sf_count_t>(sf_readf_short, sf.get(), (short *)buffer.ptr(), len);
for (int i = 0; i < framesRead; i++)
((short *)data)[i] =
((short *)buffer.ptr())[(info.channels * i) + mAliasChannel];
@ -140,18 +134,13 @@ int PCMAliasBlockFile::ReadData(samplePtr data, sampleFormat format,
// Otherwise, let libsndfile handle the conversion and
// scaling, and pass us normalized data as floats. We can
// then convert to whatever format we want.
ODManager::LockLibSndFileMutex();
framesRead = sf_readf_float(sf, (float *)buffer.ptr(), len);
ODManager::UnlockLibSndFileMutex();
framesRead = SFCall<sf_count_t>(sf_readf_float, sf.get(), (float *)buffer.ptr(), len);
float *bufferPtr = &((float *)buffer.ptr())[mAliasChannel];
CopySamples((samplePtr)bufferPtr, floatSample,
(samplePtr)data, format,
framesRead, true, info.channels);
}
ODManager::LockLibSndFileMutex();
sf_close(sf);
ODManager::UnlockLibSndFileMutex();
return framesRead;
}

View File

@ -407,7 +407,7 @@ int SimpleBlockFile::ReadData(samplePtr data, sampleFormat format,
SF_INFO info;
wxFile f; // will be closed when it goes out of scope
SNDFILE *sf = NULL;
SFFile sf;
{
Maybe<wxLogNull> silence{};
if (mSilentLog)
@ -419,7 +419,7 @@ int SimpleBlockFile::ReadData(samplePtr data, sampleFormat format,
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
sf = sf_open_fd(f.fd(), SFM_READ, &info, FALSE);
sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, FALSE));
}
if (!sf) {
@ -433,7 +433,7 @@ int SimpleBlockFile::ReadData(samplePtr data, sampleFormat format,
}
mSilentLog=FALSE;
sf_seek(sf, start, SEEK_SET);
SFCall<sf_count_t>(sf_seek, sf.get(), start, SEEK_SET);
SampleBuffer buffer(len, floatSample);
int framesRead = 0;
@ -443,13 +443,13 @@ int SimpleBlockFile::ReadData(samplePtr data, sampleFormat format,
// converting to float and back, which is unneccesary)
if (format == int16Sample &&
sf_subtype_is_integer(info.format)) {
framesRead = sf_readf_short(sf, (short *)data, len);
framesRead = SFCall<sf_count_t>(sf_readf_short, sf.get(), (short *)data, len);
}
else
if (format == int24Sample &&
sf_subtype_is_integer(info.format))
{
framesRead = sf_readf_int(sf, (int *)data, len);
framesRead = SFCall<sf_count_t>(sf_readf_int, sf.get(), (int *)data, len);
// libsndfile gave us the 3 byte sample in the 3 most
// significant bytes -- we want it in the 3 least
@ -462,13 +462,11 @@ int SimpleBlockFile::ReadData(samplePtr data, sampleFormat format,
// Otherwise, let libsndfile handle the conversion and
// scaling, and pass us normalized data as floats. We can
// then convert to whatever format we want.
framesRead = sf_readf_float(sf, (float *)buffer.ptr(), len);
framesRead = SFCall<sf_count_t>(sf_readf_float, sf.get(), (float *)buffer.ptr(), len);
CopySamples(buffer.ptr(), floatSample,
(samplePtr)data, format, framesRead);
}
sf_close(sf);
return framesRead;
}
}

View File

@ -250,8 +250,9 @@ void ExportPCMOptions::OnHeaderChoice(wxCommandEvent & WXUNUSED(evt))
bool valid = ValidatePair(fmt);
if (valid)
{
mEncodingNames.Add(sf_encoding_index_name(i));
mEncodingChoice->Append(sf_encoding_index_name(i));
const auto name = sf_encoding_index_name(i);
mEncodingNames.Add(name);
mEncodingChoice->Append(name);
mEncodingFormats.Add(enc);
for (j = 0; j < sfnum; j++)
{
@ -405,148 +406,127 @@ int ExportPCM::Export(AudacityProject *project,
sf_format = kFormats[subformat].format;
}
wxString formatStr;
SF_INFO info;
SNDFILE *sf = NULL;
int err;
//This whole operation should not occur while a file is being loaded on OD,
//(we are worried about reading from a file being written to,) so we block.
//Furthermore, we need to do this because libsndfile is not threadsafe.
ODManager::LockLibSndFileMutex();
formatStr = sf_header_name(sf_format & SF_FORMAT_TYPEMASK);
ODManager::UnlockLibSndFileMutex();
// Use libsndfile to export file
info.samplerate = (unsigned int)(rate + 0.5);
info.frames = (unsigned int)((t1 - t0)*rate + 0.5);
info.channels = numChannels;
info.format = sf_format;
info.sections = 1;
info.seekable = 0;
// If we can't export exactly the format they requested,
// try the default format for that header type...
if (!sf_format_check(&info))
info.format = (info.format & SF_FORMAT_TYPEMASK);
if (!sf_format_check(&info)) {
wxMessageBox(_("Cannot export audio in this format."));
return false;
}
wxFile f; // will be closed when it goes out of scope
if (f.Open(fName, wxFile::write)) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
ODManager::LockLibSndFileMutex();
sf = sf_open_fd(f.fd(), SFM_WRITE, &info, FALSE);
//add clipping for integer formats. We allow floats to clip.
sf_command(sf, SFC_SET_CLIPPING, NULL,sf_subtype_is_integer(sf_format)?SF_TRUE:SF_FALSE) ;
ODManager::UnlockLibSndFileMutex();
}
if (!sf) {
wxMessageBox(wxString::Format(_("Cannot export audio to %s"),
fName.c_str()));
return false;
}
// Retrieve tags if not given a set
if (metadata == NULL)
metadata = project->GetTags();
// Install the metata at the beginning of the file (except for
// WAV and WAVEX formats)
if ((sf_format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV &&
(sf_format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf, metadata, sf_format)) {
sf_close(sf);
return false;
}
}
sampleFormat format;
if (sf_subtype_more_than_16_bits(info.format))
format = floatSample;
else
format = int16Sample;
int maxBlockLen = 44100 * 5;
int updateResult = eProgressSuccess;
const WaveTrackConstArray waveTracks =
tracks->GetWaveTrackConstArray(selectionOnly, false);
{
auto mixer = CreateMixer(waveTracks,
tracks->GetTimeTrack(),
t0, t1,
info.channels, maxBlockLen, true,
rate, format, true, mixerSpec);
wxFile f; // will be closed when it goes out of scope
SFFile sf; // wraps f
ProgressDialog progress(wxFileName(fName).GetName(),
selectionOnly ?
wxString::Format(_("Exporting the selected audio as %s"),
formatStr.c_str()) :
wxString::Format(_("Exporting the entire project as %s"),
formatStr.c_str()));
wxString formatStr;
SF_INFO info;
int err;
while (updateResult == eProgressSuccess) {
sampleCount samplesWritten;
sampleCount numSamples = mixer->Process(maxBlockLen);
//This whole operation should not occur while a file is being loaded on OD,
//(we are worried about reading from a file being written to,) so we block.
//Furthermore, we need to do this because libsndfile is not threadsafe.
formatStr = SFCall<wxString>(sf_header_name, sf_format & SF_FORMAT_TYPEMASK);
if (numSamples == 0)
break;
// Use libsndfile to export file
samplePtr mixed = mixer->GetBuffer();
info.samplerate = (unsigned int)(rate + 0.5);
info.frames = (unsigned int)((t1 - t0)*rate + 0.5);
info.channels = numChannels;
info.format = sf_format;
info.sections = 1;
info.seekable = 0;
ODManager::LockLibSndFileMutex();
if (format == int16Sample)
samplesWritten = sf_writef_short(sf, (short *)mixed, numSamples);
else
samplesWritten = sf_writef_float(sf, (float *)mixed, numSamples);
ODManager::UnlockLibSndFileMutex();
if (samplesWritten != numSamples) {
char buffer2[1000];
sf_error_str(sf, buffer2, 1000);
wxMessageBox(wxString::Format(
/* i18n-hint: %s will be the error message from libsndfile, which
* is usually something unhelpful (and untranslated) like "system
* error" */
_("Error while writing %s file (disk full?).\nLibsndfile says \"%s\""),
formatStr.c_str(),
wxString::FromAscii(buffer2).c_str()));
break;
}
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
}
}
// Install the WAV metata in a "LIST" chunk at the end of the file
if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV ||
(sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf, metadata, sf_format)) {
sf_close(sf);
// If we can't export exactly the format they requested,
// try the default format for that header type...
if (!sf_format_check(&info))
info.format = (info.format & SF_FORMAT_TYPEMASK);
if (!sf_format_check(&info)) {
wxMessageBox(_("Cannot export audio in this format."));
return false;
}
}
ODManager::LockLibSndFileMutex();
err = sf_close(sf);
ODManager::UnlockLibSndFileMutex();
if (f.Open(fName, wxFile::write)) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_WRITE, &info, FALSE));
//add clipping for integer formats. We allow floats to clip.
sf_command(sf.get(), SFC_SET_CLIPPING, NULL, sf_subtype_is_integer(sf_format)?SF_TRUE:SF_FALSE) ;
}
if (err) {
char buffer[1000];
sf_error_str(sf, buffer, 1000);
wxMessageBox(wxString::Format
/* i18n-hint: %s will be the error message from libsndfile */
(_("Error (file may not have been written): %s"),
buffer));
if (!sf) {
wxMessageBox(wxString::Format(_("Cannot export audio to %s"),
fName.c_str()));
return false;
}
// Retrieve tags if not given a set
if (metadata == NULL)
metadata = project->GetTags();
// Install the metata at the beginning of the file (except for
// WAV and WAVEX formats)
if ((sf_format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV &&
(sf_format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf.get(), metadata, sf_format)) {
return false;
}
}
sampleFormat format;
if (sf_subtype_more_than_16_bits(info.format))
format = floatSample;
else
format = int16Sample;
int maxBlockLen = 44100 * 5;
const WaveTrackConstArray waveTracks =
tracks->GetWaveTrackConstArray(selectionOnly, false);
{
auto mixer = CreateMixer(waveTracks,
tracks->GetTimeTrack(),
t0, t1,
info.channels, maxBlockLen, true,
rate, format, true, mixerSpec);
ProgressDialog progress(wxFileName(fName).GetName(),
selectionOnly ?
wxString::Format(_("Exporting the selected audio as %s"),
formatStr.c_str()) :
wxString::Format(_("Exporting the entire project as %s"),
formatStr.c_str()));
while (updateResult == eProgressSuccess) {
sampleCount samplesWritten;
sampleCount numSamples = mixer->Process(maxBlockLen);
if (numSamples == 0)
break;
samplePtr mixed = mixer->GetBuffer();
if (format == int16Sample)
samplesWritten = SFCall<sf_count_t>(sf_writef_short, sf.get(), (short *)mixed, numSamples);
else
samplesWritten = SFCall<sf_count_t>(sf_writef_float, sf.get(), (float *)mixed, numSamples);
if (samplesWritten != numSamples) {
char buffer2[1000];
sf_error_str(sf.get(), buffer2, 1000);
wxMessageBox(wxString::Format(
/* i18n-hint: %s will be the error message from libsndfile, which
* is usually something unhelpful (and untranslated) like "system
* error" */
_("Error while writing %s file (disk full?).\nLibsndfile says \"%s\""),
formatStr.c_str(),
wxString::FromAscii(buffer2).c_str()));
break;
}
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
}
}
// Install the WAV metata in a "LIST" chunk at the end of the file
if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV ||
(sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf.get(), metadata, sf_format)) {
return false;
}
}
}
if (((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||

View File

@ -89,7 +89,7 @@ public:
class PCMImportFileHandle final : public ImportFileHandle
{
public:
PCMImportFileHandle(wxString name, SNDFILE *file, SF_INFO info);
PCMImportFileHandle(wxString name, SFFile &&file, SF_INFO info);
~PCMImportFileHandle();
wxString GetFileDescription();
@ -108,7 +108,7 @@ public:
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)){}
private:
SNDFILE *mFile;
SFFile mFile;
SF_INFO mInfo;
sampleFormat mFormat;
};
@ -127,11 +127,11 @@ wxString PCMImportPlugin::GetPluginFormatDescription()
std::unique_ptr<ImportFileHandle> PCMImportPlugin::Open(const wxString &filename)
{
SF_INFO info;
SNDFILE *file = NULL;
wxFile f; // will be closed when it goes out of scope
SFFile file;
memset(&info, 0, sizeof(info));
wxFile f; // will be closed when it goes out of scope
#ifdef __WXGTK__
if (filename.Lower().EndsWith(wxT("mp3"))) {
@ -154,11 +154,11 @@ std::unique_ptr<ImportFileHandle> PCMImportPlugin::Open(const wxString &filename
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
file = sf_open_fd(f.fd(), SFM_READ, &info, TRUE);
file.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, TRUE));
}
// The file descriptor is now owned by "file", so we must tell "f" to leave
// it alone. The file descriptor is closed by sf_open_fd() even if an error
// it alone. The file descriptor is closed by the destructor of file even if an error
// occurs.
f.Detach();
@ -184,13 +184,14 @@ std::unique_ptr<ImportFileHandle> PCMImportPlugin::Open(const wxString &filename
return nullptr;
}
return std::make_unique<PCMImportFileHandle>(filename, file, info);
// Success, so now transfer the duty to close the file from "file".
return std::make_unique<PCMImportFileHandle>(filename, std::move(file), info);
}
PCMImportFileHandle::PCMImportFileHandle(wxString name,
SNDFILE *file, SF_INFO info)
SFFile &&file, SF_INFO info)
: ImportFileHandle(name),
mFile(file),
mFile(std::move(file)),
mInfo(info)
{
//
@ -211,7 +212,7 @@ PCMImportFileHandle::PCMImportFileHandle(wxString name,
wxString PCMImportFileHandle::GetFileDescription()
{
return sf_header_name(mInfo.format);
return SFCall<wxString>(sf_header_name, mInfo.format);
}
int PCMImportFileHandle::GetFileUncompressedBytes()
@ -327,7 +328,7 @@ int PCMImportFileHandle::Import(TrackFactory *trackFactory,
{
outTracks.clear();
wxASSERT(mFile);
wxASSERT(mFile.get());
// Get the preference / warn the user about aliased files.
wxString copyEdit = AskCopyOrEdit();
@ -454,10 +455,10 @@ int PCMImportFileHandle::Import(TrackFactory *trackFactory,
block = maxBlock;
if (mFormat == int16Sample)
block = sf_readf_short(mFile, (short *)srcbuffer.ptr(), block);
block = SFCall<sf_count_t>(sf_readf_short, mFile.get(), (short *)srcbuffer.ptr(), block);
//import 24 bit int as float and have the append function convert it. This is how PCMAliasBlockFile works too.
else
block = sf_readf_float(mFile, (float *)srcbuffer.ptr(), block);
block = SFCall<sf_count_t>(sf_readf_float, mFile.get(), (float *)srcbuffer.ptr(), block);
if (block) {
auto iter = channels.begin();
@ -497,47 +498,47 @@ int PCMImportFileHandle::Import(TrackFactory *trackFactory,
const char *str;
str = sf_get_string(mFile, SF_STR_TITLE);
str = sf_get_string(mFile.get(), SF_STR_TITLE);
if (str) {
tags->SetTag(TAG_TITLE, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_ALBUM);
str = sf_get_string(mFile.get(), SF_STR_ALBUM);
if (str) {
tags->SetTag(TAG_ALBUM, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_ARTIST);
str = sf_get_string(mFile.get(), SF_STR_ARTIST);
if (str) {
tags->SetTag(TAG_ARTIST, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_COMMENT);
str = sf_get_string(mFile.get(), SF_STR_COMMENT);
if (str) {
tags->SetTag(TAG_COMMENTS, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_DATE);
str = sf_get_string(mFile.get(), SF_STR_DATE);
if (str) {
tags->SetTag(TAG_YEAR, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_COPYRIGHT);
str = sf_get_string(mFile.get(), SF_STR_COPYRIGHT);
if (str) {
tags->SetTag(TAG_COPYRIGHT, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_SOFTWARE);
str = sf_get_string(mFile.get(), SF_STR_SOFTWARE);
if (str) {
tags->SetTag(TAG_SOFTWARE, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_TRACKNUMBER);
str = sf_get_string(mFile.get(), SF_STR_TRACKNUMBER);
if (str) {
tags->SetTag(TAG_TRACK, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_GENRE);
str = sf_get_string(mFile.get(), SF_STR_GENRE);
if (str) {
tags->SetTag(TAG_GENRE, UTF8CTOWX(str));
}
@ -690,5 +691,4 @@ int PCMImportFileHandle::Import(TrackFactory *trackFactory,
PCMImportFileHandle::~PCMImportFileHandle()
{
sf_close(mFile);
}

View File

@ -98,175 +98,177 @@ void ImportRaw(wxWindow *parent, const wxString &fileName,
{
outTracks.clear();
int encoding = 0; // Guess Format
int numChannels = 0;
sampleFormat format;
sf_count_t offset = 0;
sampleCount totalFrames;
double rate = 44100.0;
double percent = 100.0;
SNDFILE *sndFile = NULL;
SF_INFO sndInfo;
int result;
try {
// Yes, FormatClassifier currently handles filenames in UTF8 format only, that's
// a TODO ...
FormatClassifier theClassifier(fileName.utf8_str());
encoding = theClassifier.GetResultFormatLibSndfile();
numChannels = theClassifier.GetResultChannels();
offset = 0;
} catch (...) {
// Something went wrong in FormatClassifier, use defaults instead.
encoding = 0;
}
if (encoding <= 0) {
// Unable to guess. Use mono, 16-bit samples with CPU endianness
// as the default.
encoding = SF_FORMAT_RAW | SF_ENDIAN_CPU | SF_FORMAT_PCM_16;
numChannels = 1;
offset = 0;
}
ImportRawDialog dlog(parent, encoding, numChannels, (int)offset, rate);
dlog.ShowModal();
if (!dlog.GetReturnCode())
return;
encoding = dlog.mEncoding;
numChannels = dlog.mChannels;
rate = dlog.mRate;
offset = (sf_count_t)dlog.mOffset;
percent = dlog.mPercent;
memset(&sndInfo, 0, sizeof(SF_INFO));
sndInfo.samplerate = (int)rate;
sndInfo.channels = (int)numChannels;
sndInfo.format = encoding | SF_FORMAT_RAW;
wxFile f; // will be closed when it goes out of scope
if (f.Open(fileName)) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
sndFile = sf_open_fd(f.fd(), SFM_READ, &sndInfo, FALSE);
}
if (!sndFile){
// TODO: Handle error
char str[1000];
sf_error_str((SNDFILE *)NULL, str, 1000);
printf("%s\n", str);
return;
}
result = sf_command(sndFile, SFC_SET_RAW_START_OFFSET, &offset, sizeof(offset));
if (result != 0) {
char str[1000];
sf_error_str(sndFile, str, 1000);
printf("%s\n", str);
}
sf_seek(sndFile, 0, SEEK_SET);
totalFrames = (sampleCount)(sndInfo.frames * percent / 100.0);
//
// Sample format:
//
// In general, go with the user's preferences. However, if
// the file is higher-quality, go with a format which preserves
// the quality of the original file.
//
format = (sampleFormat)
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
if (format != floatSample &&
sf_subtype_more_than_16_bits(encoding))
format = floatSample;
TrackHolders channels(numChannels);
auto iter = channels.begin();
for (int c = 0; c < numChannels; ++iter, ++c) {
const auto channel =
(*iter = trackFactory->NewWaveTrack(format, rate)).get();
if (numChannels > 1)
switch (c) {
case 0:
channel->SetChannel(Track::LeftChannel);
break;
case 1:
channel->SetChannel(Track::RightChannel);
break;
default:
channel->SetChannel(Track::MonoChannel);
}
}
const auto firstChannel = channels.begin()->get();
if (numChannels == 2) {
firstChannel->SetLinked(true);
}
sampleCount maxBlockSize = firstChannel->GetMaxBlockSize();
TrackHolders channels;
int updateResult = eProgressSuccess;
SampleBuffer srcbuffer(maxBlockSize * numChannels, format);
SampleBuffer buffer(maxBlockSize, format);
sampleCount framescompleted = 0;
wxString msg;
msg.Printf(_("Importing %s"), wxFileName::FileName(fileName).GetFullName().c_str());
/* i18n-hint: 'Raw' means 'unprocessed' here and should usually be tanslated.*/
ProgressDialog progress(_("Import Raw"), msg);
long block;
do {
block = maxBlockSize;
{
SF_INFO sndInfo;
int result;
if (block + framescompleted > totalFrames)
block = totalFrames - framescompleted;
int numChannels = 0;
if (format == int16Sample)
block = sf_readf_short(sndFile, (short *)srcbuffer.ptr(), block);
else
block = sf_readf_float(sndFile, (float *)srcbuffer.ptr(), block);
if (block) {
auto iter = channels.begin();
for(int c=0; c<numChannels; ++iter, ++c) {
if (format==int16Sample) {
for(int j=0; j<block; j++)
((short *)buffer.ptr())[j] =
((short *)srcbuffer.ptr())[numChannels*j+c];
}
else {
for(int j=0; j<block; j++)
((float *)buffer.ptr())[j] =
((float *)srcbuffer.ptr())[numChannels*j+c];
}
iter->get()->Append(buffer.ptr(), format, block);
}
framescompleted += block;
try {
// Yes, FormatClassifier currently handles filenames in UTF8 format only, that's
// a TODO ...
FormatClassifier theClassifier(fileName.utf8_str());
encoding = theClassifier.GetResultFormatLibSndfile();
numChannels = theClassifier.GetResultChannels();
offset = 0;
} catch (...) {
// Something went wrong in FormatClassifier, use defaults instead.
encoding = 0;
}
updateResult = progress.Update((wxULongLong_t)framescompleted,
(wxULongLong_t)totalFrames);
if (updateResult != eProgressSuccess)
break;
if (encoding <= 0) {
// Unable to guess. Use mono, 16-bit samples with CPU endianness
// as the default.
encoding = SF_FORMAT_RAW | SF_ENDIAN_CPU | SF_FORMAT_PCM_16;
numChannels = 1;
offset = 0;
}
} while (block > 0 && framescompleted < totalFrames);
ImportRawDialog dlog(parent, encoding, numChannels, (int)offset, rate);
dlog.ShowModal();
if (!dlog.GetReturnCode())
return;
sf_close(sndFile);
encoding = dlog.mEncoding;
numChannels = dlog.mChannels;
rate = dlog.mRate;
offset = (sf_count_t)dlog.mOffset;
percent = dlog.mPercent;
memset(&sndInfo, 0, sizeof(SF_INFO));
sndInfo.samplerate = (int)rate;
sndInfo.channels = (int)numChannels;
sndInfo.format = encoding | SF_FORMAT_RAW;
wxFile f; // will be closed when it goes out of scope
SFFile sndFile;
if (f.Open(fileName)) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
sndFile.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &sndInfo, FALSE));
}
if (!sndFile){
// TODO: Handle error
char str[1000];
sf_error_str((SNDFILE *)NULL, str, 1000);
printf("%s\n", str);
return;
}
result = sf_command(sndFile.get(), SFC_SET_RAW_START_OFFSET, &offset, sizeof(offset));
if (result != 0) {
char str[1000];
sf_error_str(sndFile.get(), str, 1000);
printf("%s\n", str);
}
SFCall<sf_count_t>(sf_seek, sndFile.get(), 0, SEEK_SET);
totalFrames = (sampleCount)(sndInfo.frames * percent / 100.0);
//
// Sample format:
//
// In general, go with the user's preferences. However, if
// the file is higher-quality, go with a format which preserves
// the quality of the original file.
//
format = (sampleFormat)
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
if (format != floatSample &&
sf_subtype_more_than_16_bits(encoding))
format = floatSample;
channels.resize(numChannels);
auto iter = channels.begin();
for (int c = 0; c < numChannels; ++iter, ++c) {
const auto channel =
(*iter = trackFactory->NewWaveTrack(format, rate)).get();
if (numChannels > 1)
switch (c) {
case 0:
channel->SetChannel(Track::LeftChannel);
break;
case 1:
channel->SetChannel(Track::RightChannel);
break;
default:
channel->SetChannel(Track::MonoChannel);
}
}
const auto firstChannel = channels.begin()->get();
if (numChannels == 2) {
firstChannel->SetLinked(true);
}
sampleCount maxBlockSize = firstChannel->GetMaxBlockSize();
SampleBuffer srcbuffer(maxBlockSize * numChannels, format);
SampleBuffer buffer(maxBlockSize, format);
sampleCount framescompleted = 0;
wxString msg;
msg.Printf(_("Importing %s"), wxFileName::FileName(fileName).GetFullName().c_str());
/* i18n-hint: 'Raw' means 'unprocessed' here and should usually be tanslated.*/
ProgressDialog progress(_("Import Raw"), msg);
do {
block = maxBlockSize;
if (block + framescompleted > totalFrames)
block = totalFrames - framescompleted;
if (format == int16Sample)
block = SFCall<sf_count_t>(sf_readf_short, sndFile.get(), (short *)srcbuffer.ptr(), block);
else
block = SFCall<sf_count_t>(sf_readf_float, sndFile.get(), (float *)srcbuffer.ptr(), block);
if (block) {
auto iter = channels.begin();
for(int c=0; c<numChannels; ++iter, ++c) {
if (format==int16Sample) {
for(int j=0; j<block; j++)
((short *)buffer.ptr())[j] =
((short *)srcbuffer.ptr())[numChannels*j+c];
}
else {
for(int j=0; j<block; j++)
((float *)buffer.ptr())[j] =
((float *)srcbuffer.ptr())[numChannels*j+c];
}
iter->get()->Append(buffer.ptr(), format, block);
}
framescompleted += block;
}
updateResult = progress.Update((wxULongLong_t)framescompleted,
(wxULongLong_t)totalFrames);
if (updateResult != eProgressSuccess)
break;
} while (block > 0 && framescompleted < totalFrames);
}
int res = updateResult;
if (block < 0)

View File

@ -51,17 +51,6 @@ int CompareNoCaseFileName(const wxString& first, const wxString& second)
return first.CmpNoCase(second);
}
void ODManager::LockLibSndFileMutex()
{
sLibSndFileMutex.Lock();
}
void ODManager::UnlockLibSndFileMutex()
{
sLibSndFileMutex.Unlock();
}
//private constructor - Singleton.
ODManager::ODManager()
{

View File

@ -119,9 +119,6 @@ class ODManager final
Pauser &operator= (const Pauser&) PROHIBITED;
};
static void LockLibSndFileMutex();
static void UnlockLibSndFileMutex();
protected: