1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-13 22:21:11 +02:00
Files
audacity/src/import/ImportRaw.cpp
Paul Licameli 79c79f9cd3 Remove many mentions of sampleCount with auto and decltype...
... This makes much code agnostic about how other things (functions and
arguments) are typed.

Many of these neeed to become size_t instead of sampleCount.
2016-08-24 14:50:45 -04:00

531 lines
15 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
ImportRaw.cpp
Dominic Mazzoni
*******************************************************************//**
\file ImportRaw.cpp
\brief Functions for guessing audio type and attempting to read from
unknown sample audio data. Implements ImportRawDialog.
*//****************************************************************//**
\class ImportRawDialog
\brief ImportRawDialog prompts you with options such as endianness
and sample size to help you importing data of an unknown format.
*//*******************************************************************/
#include "../Audacity.h"
#include "ImportRaw.h"
#include "Import.h"
#include "../DirManager.h"
#include "../FileFormats.h"
#include "../Internat.h"
#include "../Prefs.h"
#include "../ShuttleGui.h"
#include "../WaveTrack.h"
#include <cmath>
#include <cstdio>
#include <stdint.h>
#include <vector>
#include <wx/defs.h>
#include <wx/button.h>
#include <wx/choice.h>
#include <wx/intl.h>
#include <wx/msgdlg.h>
#include <wx/panel.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/timer.h>
#include "RawAudioGuess.h"
#include "MultiFormatReader.h"
#include "SpecPowerMeter.h"
#include "FormatClassifier.h"
#include "sndfile.h"
class ImportRawDialog final : public wxDialogWrapper {
public:
ImportRawDialog(wxWindow * parent,
int encoding, int channels,
int offset, double rate);
~ImportRawDialog();
void OnOK(wxCommandEvent & event);
void OnCancel(wxCommandEvent & event);
void OnPlay(wxCommandEvent & event);
void OnChoice(wxCommandEvent & event);
// in and out
int mEncoding;
int mChannels;
int mOffset;
double mRate;
double mPercent;
private:
wxButton *mOK;
wxChoice *mEncodingChoice;
wxChoice *mEndianChoice;
wxChoice *mChannelChoice;
wxTextCtrl *mOffsetText;
wxTextCtrl *mPercentText;
wxTextCtrl *mRateText;
int mNumEncodings;
int *mEncodingSubtype;
DECLARE_EVENT_TABLE()
};
void ImportRaw(wxWindow *parent, const wxString &fileName,
TrackFactory *trackFactory, TrackHolders &outTracks)
{
outTracks.clear();
int encoding = 0; // Guess Format
sampleFormat format;
sf_count_t offset = 0;
double rate = 44100.0;
double percent = 100.0;
TrackHolders channels;
int updateResult = eProgressSuccess;
{
SF_INFO sndInfo;
int result;
int numChannels = 0;
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
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);
auto 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);
}
auto maxBlockSize = firstChannel->GetMaxBlockSize();
SampleBuffer srcbuffer(maxBlockSize * numChannels, format);
SampleBuffer buffer(maxBlockSize, format);
decltype(totalFrames) framescompleted = 0;
if (totalFrames < 0) {
wxASSERT(false);
totalFrames = 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);
size_t block;
do {
block =
limitSampleBufferSize( maxBlockSize, totalFrames - framescompleted );
sf_count_t result;
if (format == int16Sample)
result = SFCall<sf_count_t>(sf_readf_short, sndFile.get(), (short *)srcbuffer.ptr(), block);
else
result = SFCall<sf_count_t>(sf_readf_float, sndFile.get(), (float *)srcbuffer.ptr(), block);
if (result >= 0) {
block = result;
}
else {
// This is not supposed to happen, sndfile.h says result is always
// a count, not an invalid value for error
wxASSERT(false);
updateResult = eProgressFailed;
break;
}
if (block) {
auto iter = channels.begin();
for(int c=0; c<numChannels; ++iter, ++c) {
if (format==int16Sample) {
for(decltype(block) j=0; j<block; j++)
((short *)buffer.ptr())[j] =
((short *)srcbuffer.ptr())[numChannels*j+c];
}
else {
for(decltype(block) 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);
}
if (updateResult == eProgressFailed || updateResult == eProgressCancelled) {
// It's a shame we can't return proper error code
return;
}
for (const auto &channel : channels)
channel->Flush();
outTracks.swap(channels);
}
//
// ImportRawDialog
//
enum {
ChoiceID = 9000,
PlayID
};
BEGIN_EVENT_TABLE(ImportRawDialog, wxDialogWrapper)
EVT_BUTTON(wxID_OK, ImportRawDialog::OnOK)
EVT_BUTTON(wxID_CANCEL, ImportRawDialog::OnCancel)
EVT_BUTTON(PlayID, ImportRawDialog::OnPlay)
EVT_CHOICE(ChoiceID, ImportRawDialog::OnChoice)
END_EVENT_TABLE()
ImportRawDialog::ImportRawDialog(wxWindow * parent,
int encoding, int channels,
int offset, double rate)
: wxDialogWrapper(parent, wxID_ANY, _("Import Raw Data"),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
mEncoding(encoding),
mChannels(channels),
mOffset(offset),
mRate(rate)
{
SetName(GetTitle());
ShuttleGui S(this, eIsCreating);
wxArrayString encodings;
wxArrayString endians;
wxArrayString chans;
int num;
int selection;
int endian;
int i;
num = sf_num_encodings();
mNumEncodings = 0;
mEncodingSubtype = new int[num];
selection = 0;
for (i=0; i<num; i++) {
SF_INFO info;
memset(&info, 0, sizeof(SF_INFO));
int subtype = sf_encoding_index_to_subtype(i);
info.format = SF_FORMAT_RAW + SF_ENDIAN_LITTLE + subtype;
info.channels = 1;
info.samplerate = 44100;
if (sf_format_check(&info)) {
mEncodingSubtype[mNumEncodings] = subtype;
encodings.Add(sf_encoding_index_name(i));
if ((mEncoding & SF_FORMAT_SUBMASK) == subtype)
selection = mNumEncodings;
mNumEncodings++;
}
}
/* i18n-hint: Refers to byte-order. Don't translate "endianness" if you don't
know the correct technical word. */
endians.Add(_("No endianness"));
/* i18n-hint: Refers to byte-order. Don't translate this if you don't
know the correct technical word. */
endians.Add(_("Little-endian"));
/* i18n-hint: Refers to byte-order. Don't translate this if you don't
know the correct technical word. */
endians.Add(_("Big-endian"));
/* i18n-hint: Refers to byte-order. Don't translate "endianness" if you don't
know the correct technical word. */
endians.Add(_("Default endianness"));
switch (mEncoding & (SF_FORMAT_ENDMASK))
{
default:
case SF_ENDIAN_FILE:
endian = 0;
break;
case SF_ENDIAN_LITTLE:
endian = 1;
break;
case SF_ENDIAN_BIG:
endian = 2;
break;
case SF_ENDIAN_CPU:
endian = 3;
break;
}
chans.Add(_("1 Channel (Mono)"));
chans.Add(_("2 Channels (Stereo)"));
for (i=2; i<16; i++) {
chans.Add(wxString::Format(_("%d Channels"), i + 1));
}
S.StartVerticalLay(false);
{
S.SetBorder(5);
S.StartTwoColumn();
{
mEncodingChoice = S.Id(ChoiceID).AddChoice(_("Encoding:"),
encodings[selection],
&encodings);
mEndianChoice = S.Id(ChoiceID).AddChoice(_("Byte order:"),
endians[endian],
&endians);
mChannelChoice = S.Id(ChoiceID).AddChoice(_("Channels:"),
chans[mChannels-1],
&chans);
}
S.EndTwoColumn();
S.SetBorder(5);
S.StartMultiColumn(3);
{
// Offset text
/* i18n-hint: (noun)*/
mOffsetText = S.AddTextBox(_("Start offset:"),
wxString::Format(wxT("%d"), mOffset),
12);
S.AddUnits(_("bytes"));
// Percent text
mPercentText = S.AddTextBox(_("Amount to import:"),
wxT("100"),
12);
S.AddUnits(wxT("%"));
// Rate text
/* i18n-hint: (noun)*/
mRateText = S.AddTextBox(_("Sample rate:"),
wxString::Format(wxT("%d"), (int)mRate),
12);
/* i18n-hint: This is the abbreviation for "Hertz", or
cycles per second. */
S.AddUnits(_("Hz"));
}
S.EndMultiColumn();
//
// Preview Pane goes here
//
S.AddStandardButtons();
// Find the OK button, and change its text to 'Import'.
// We MUST set mOK because it is used later.
mOK = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
mOK->SetLabel(_("&Import"));
}
S.EndVerticalLay();
Fit();
SetSizeHints(GetSize());
Centre(wxBOTH);
}
ImportRawDialog::~ImportRawDialog()
{
delete[] mEncodingSubtype;
}
void ImportRawDialog::OnOK(wxCommandEvent & WXUNUSED(event))
{
long l;
mEncoding = mEncodingSubtype[mEncodingChoice->GetSelection()];
mEncoding += (mEndianChoice->GetSelection() * 0x10000000);
mChannels = mChannelChoice->GetSelection() + 1;
mOffsetText->GetValue().ToLong(&l);
mOffset = l;
mPercentText->GetValue().ToDouble(&mPercent);
mRateText->GetValue().ToDouble(&mRate);
if (mChannels < 1 || mChannels > 16)
mChannels = 1;
if (mOffset < 0)
mOffset = 0;
if (mPercent < 0.0)
mPercent = 0.0;
if (mPercent > 100.0)
mPercent = 100.0;
if (mRate < 100.0)
mRate = 100.0;
if (mRate > 100000.0)
mRate = 100000.0;
EndModal(true);
}
void ImportRawDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
{
EndModal(false);
}
void ImportRawDialog::OnPlay(wxCommandEvent & WXUNUSED(event))
{
}
void ImportRawDialog::OnChoice(wxCommandEvent & WXUNUSED(event))
{
SF_INFO info;
memset(&info, 0, sizeof(SF_INFO));
mEncoding = mEncodingSubtype[mEncodingChoice->GetSelection()];
mEncoding += (mEndianChoice->GetSelection() * 0x10000000);
info.format = mEncoding | SF_FORMAT_RAW;
info.channels = mChannelChoice->GetSelection() + 1;
info.samplerate = 44100;
//mOK = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
if (sf_format_check(&info)) {
mOK->Enable(true);
return;
}
// Try it with 1-channel
info.channels = 1;
if (sf_format_check(&info)) {
mChannelChoice->SetSelection(0);
mOK->Enable(true);
return;
}
// Otherwise, this is an unsupported format
mOK->Enable(false);
}