mirror of
https://github.com/cookiengineer/audacity
synced 2026-03-04 13:40:58 +01:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user