Audacity 3.2.0
ImportRaw.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ImportRaw.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//****************************************************************//*******************************************************************/
22
23
24
25#include "ImportRaw.h"
26
27#include "ImportPlugin.h"
28#include "ImportUtils.h"
29
30#include "AudioIOBase.h"
31#include "FileFormats.h"
32#include "Prefs.h"
33#include "ProjectRate.h"
34#include "SelectFile.h"
35#include "ShuttleGui.h"
36#include "UserException.h"
37#include "WaveTrack.h"
38#include "ProgressDialog.h"
39
40#include <cmath>
41#include <stdint.h>
42#include <vector>
43
44#include <wx/crt.h>
45#include <wx/defs.h>
46#include <wx/button.h>
47#include <wx/choice.h>
48#include <wx/combobox.h>
49#include <wx/panel.h>
50#include <wx/stattext.h>
51#include <wx/textctrl.h>
52
53// #include "RawAudioGuess.h"
54#include "FormatClassifier.h"
55
56#include "sndfile.h"
57
58class ImportRawDialog final : public wxDialogWrapper {
59
60 public:
61 ImportRawDialog(wxWindow * parent, const wxString & fileName);
63
64 void OnOK(wxCommandEvent & event);
65 void OnCancel(wxCommandEvent & event);
66 void OnPlay(wxCommandEvent & event);
67 void OnDetect(wxCommandEvent & event);
68 void OnChoice(wxCommandEvent & event);
69
70 // in and out
71 // Make static to preserve value for next raw import
72 static int mEncoding;
73 static unsigned mChannels;
74 static long long mOffset;
75 static double mRate;
76 static double mPercent;
77
78 private:
79
80 wxButton *mOK;
81 wxChoice *mEncodingChoice;
82 wxChoice *mEndianChoice;
83 wxChoice *mChannelChoice;
84 wxTextCtrl *mOffsetText;
85 wxTextCtrl *mPercentText;
86 wxComboBox *mRateText;
87
88 std::vector<int> mEncodingSubtype;
89
90 wxString mFileName;
91
92 DECLARE_EVENT_TABLE()
93};
94
95// Initial value for Import Raw dialog
96int ImportRawDialog::mEncoding = SF_FORMAT_RAW | SF_ENDIAN_CPU | SF_FORMAT_PCM_16;
98long long ImportRawDialog::mOffset = 0;
99double ImportRawDialog::mRate = 0; // -> project rate
100double ImportRawDialog::mPercent = 100.;
101
102
103// This function leaves outTracks empty as an indication of error,
104// but may also throw FileException to make use of the application's
105// user visible error reporting.
106void ImportRaw(const AudacityProject &project, wxWindow *parent, const wxString &fileName,
107 WaveTrackFactory *trackFactory, TrackHolders &outTracks)
108{
109 outTracks.clear();
110
111 TrackListHolder trackList;
112 auto updateResult = ProgressResult::Success;
113
114 {
115 // On first run, set default sample rate from project rate
116 if (ImportRawDialog::mRate < 100.)
118
119 ImportRawDialog dlog(parent, fileName);
120 dlog.ShowModal();
121 if (!dlog.GetReturnCode())
122 return;
123
124 const int encoding = dlog.mEncoding;
125 unsigned numChannels = dlog.mChannels;
126 double rate = dlog.mRate;
127 sf_count_t offset = (sf_count_t)dlog.mOffset;
128 double percent = dlog.mPercent;
129
130 SF_INFO sndInfo = { 0 };
131 sndInfo.samplerate = (int)rate;
132 sndInfo.channels = (int)numChannels;
133 sndInfo.format = encoding | SF_FORMAT_RAW;
134
135 wxFile f; // will be closed when it goes out of scope
136 SFFile sndFile;
137
138 if (f.Open(fileName)) {
139 // Even though there is an sf_open() that takes a filename, use the one that
140 // takes a file descriptor since wxWidgets can open a file with a Unicode name and
141 // libsndfile can't (under Windows).
142 sndFile.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &sndInfo, FALSE));
143 }
144
145 if (!sndFile){
146 char str[1000];
147 sf_error_str((SNDFILE *)NULL, str, 1000);
148 wxPrintf("%s\n", str);
149
150 throw FileException{ FileException::Cause::Open, fileName };
151 }
152
153
154 {
155 int result = sf_command(sndFile.get(), SFC_SET_RAW_START_OFFSET, &offset, sizeof(offset));
156 if (result != 0) {
157 char str[1000];
158 sf_error_str(sndFile.get(), str, 1000);
159 wxPrintf("%s\n", str);
160
161 throw FileException{ FileException::Cause::Read, fileName };
162 }
163 }
164 SFCall<sf_count_t>(sf_seek, sndFile.get(), 0, SEEK_SET);
165
166 auto totalFrames =
167 // fraction of a sf_count_t value
168 (sampleCount)(sndInfo.frames * percent / 100.0);
169
170 //
171 // Sample format:
172 //
173 // In general, go with the user's preferences. However, if
174 // the file is higher-quality, go with a format which preserves
175 // the quality of the original file.
176 //
177
180
181 trackList = trackFactory->CreateMany(numChannels, format, rate);
182
183 const auto maxBlockSize = (*trackList->Any<WaveTrack>().begin())->GetMaxBlockSize();
184
185 SampleBuffer srcbuffer(maxBlockSize * numChannels, format);
187
188 decltype(totalFrames) framescompleted = 0;
189 if (totalFrames < 0) {
190 wxASSERT(false);
191 totalFrames = 0;
192 }
193
194 auto msg = XO("Importing %s").Format( wxFileName::FileName(fileName).GetFullName() );
195
196 /* i18n-hint: 'Raw' means 'unprocessed' here and should usually be translated.*/
197 ProgressDialog progress(XO("Import Raw"), msg);
198
199 size_t block;
200 do {
201 block =
202 limitSampleBufferSize( maxBlockSize, totalFrames - framescompleted );
203
204 sf_count_t sf_result;
205 if (format == int16Sample)
206 sf_result = SFCall<sf_count_t>(sf_readf_short, sndFile.get(), (short *)srcbuffer.ptr(), block);
207 else
208 sf_result = SFCall<sf_count_t>(sf_readf_float, sndFile.get(), (float *)srcbuffer.ptr(), block);
209
210 if (sf_result >= 0) {
211 block = sf_result;
212 }
213 else {
214 // This is not supposed to happen, sndfile.h says result is always
215 // a count, not an invalid value for error
216 throw FileException{ FileException::Cause::Read, fileName };
217 }
218
219 if (block) {
220 size_t c = 0;
221 ImportUtils::ForEachChannel(*trackList, [&](auto& channel)
222 {
223 if (format == int16Sample) {
224 for (size_t j = 0; j < block; ++j)
225 ((short *)buffer.ptr())[j] =
226 ((short *)srcbuffer.ptr())[numChannels * j + c];
227 }
228 else {
229 for (size_t j = 0; j < block; ++j)
230 ((float *)buffer.ptr())[j] =
231 ((float *)srcbuffer.ptr())[numChannels * j + c];
232 }
233
234 channel.AppendBuffer(buffer.ptr(),
235 ((format == int16Sample) ? int16Sample : floatSample), block,
236 1, sf_subtype_to_effective_format(encoding));
237 ++c;
238 });
239 framescompleted += block;
240 }
241
242 updateResult = progress.Update(
243 framescompleted.as_long_long(),
244 totalFrames.as_long_long()
245 );
246 if (updateResult != ProgressResult::Success)
247 break;
248
249 } while (block > 0 && framescompleted < totalFrames);
250 }
251
252 if (updateResult == ProgressResult::Failed || updateResult == ProgressResult::Cancelled)
253 throw UserException{};
254
255 ImportUtils::FinalizeImport(outTracks, move(*trackList));
256}
257
258
259// Get endian choice from SF_FORMAT
260static int getEndianChoice(int sfFormat) {
261 switch (sfFormat & SF_FORMAT_ENDMASK)
262 {
263 default:
264 case SF_ENDIAN_FILE:
265 return 0;
266 case SF_ENDIAN_LITTLE:
267 return 1;
268 case SF_ENDIAN_BIG:
269 return 2;
270 case SF_ENDIAN_CPU:
271 return 3;
272 }
273}
274
275
276//
277// ImportRawDialog
278//
279
280enum {
281 ChoiceID = 9000,
284};
285
286BEGIN_EVENT_TABLE(ImportRawDialog, wxDialogWrapper)
293
294ImportRawDialog::ImportRawDialog(wxWindow * parent, const wxString & fileName)
295: wxDialogWrapper(parent, wxID_ANY, XO("Import Raw Data"),
296 wxDefaultPosition, wxDefaultSize,
297 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
298 mFileName(fileName)
299{
300 wxASSERT(0 < mChannels && mChannels <= 16);
301
302 SetName();
303
304 // Append filename at window title
305 wxFileName wfn{ fileName };
306 wxString windowTitle = XO("%s: %s").Format(GetTitle(), wfn.GetFullName()).Translation();
307 wxDialog::SetTitle(windowTitle);
308
309 ShuttleGui S(this, eIsCreating);
310 TranslatableStrings encodings;
311
312 int num = sf_num_encodings();
313
314 int selection = 0;
315 for (int i = 0; i < num; i++) {
316 SF_INFO info = { 0 };
317
318 int subtype = sf_encoding_index_to_subtype(i);
319 info.format = SF_FORMAT_RAW + SF_ENDIAN_LITTLE + subtype;
320 info.channels = 1;
321 info.samplerate = 44100;
322
323 if (sf_format_check(&info)) {
324 mEncodingSubtype.push_back(subtype);
325 encodings.push_back( Verbatim( sf_encoding_index_name(i) ) );
326
327 if ((mEncoding & SF_FORMAT_SUBMASK) == subtype)
328 selection = mEncodingSubtype.size() - 1;
329 }
330 }
331
332 TranslatableStrings endians{
333 /* i18n-hint: Refers to byte-order. Don't translate "endianness" if you don't
334 know the correct technical word. */
335 XO("No endianness") ,
336 /* i18n-hint: Refers to byte-order. Don't translate this if you don't
337 know the correct technical word. */
338 XO("Little-endian") ,
339 /* i18n-hint: Refers to byte-order. Don't translate this if you don't
340 know the correct technical word. */
341 XO("Big-endian") ,
342 /* i18n-hint: Refers to byte-order. Don't translate "endianness" if you don't
343 know the correct technical word. */
344 XO("Default endianness") ,
345 };
346
347 int endian = getEndianChoice(mEncoding);
348
350 XO("1 Channel (Mono)") ,
351 XO("2 Channels (Stereo)") ,
352 };
353 for (int i = 2; i < 16; i++) {
354 chans.push_back( XO("%d Channels").Format( i + 1 ) );
355 }
356
357 S.StartVerticalLay(false);
358 {
359 S.SetBorder(5);
360 S.StartTwoColumn();
361 {
362 mEncodingChoice = S.Id(ChoiceID).AddChoice(XXO("Encoding:"),
363 encodings,
364 selection);
365 mEndianChoice = S.Id(ChoiceID).AddChoice(XXO("Byte order:"),
366 endians,
367 endian);
368 mChannelChoice = S.Id(ChoiceID).AddChoice(XXO("Channels:"),
369 chans,
370 mChannels - 1);
371 }
372 S.EndTwoColumn();
373
374 S.SetBorder(5);
375 S.StartMultiColumn(3);
376 {
377 // Offset text
378 /* i18n-hint: (noun)*/
379 mOffsetText = S.AddTextBox(XXO("Start offset:"),
380 wxString::Format(wxT("%d"), mOffset),
381 12);
382 S.AddUnits(XO("bytes"));
383
384 // Percent text
385 mPercentText = S.AddTextBox(XXO("Amount to import:"),
386 wxT("100"),
387 12);
388 S.AddUnits(XO("%"));
389
390 // Rate text
391 wxArrayStringEx rates;
392 for (int i = 0; i < AudioIOBase::NumStandardRates; i++) {
393 rates.Add(
394 wxString::Format(wxT("%d"), AudioIOBase::StandardRates[i]));
395 }
396
397 /* i18n-hint: (noun)*/
398 mRateText = S.AddCombo(XXO("Sample rate:"),
399 wxString::Format(wxT("%d"), (int)mRate),
400 rates);
401 /* i18n-hint: This is the abbreviation for "Hertz", or
402 cycles per second. */
403 S.AddUnits(XO("Hz"));
404 }
405 S.EndMultiColumn();
406
407 S.SetBorder(5);
408 S.StartTwoColumn();
409 {
410 /* i18n-hint: Guess format of raw file */
411 S.Id(DetectID).AddButton(XXO("Detect"));
412
413 //
414 // Preview Pane goes here
415 //
416
417 S.AddStandardButtons();
418 }
419 S.EndTwoColumn();
420
421 // Find the OK button, and change its text to 'Import'.
422 // We MUST set mOK because it is used later.
423 mOK = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
424 mOK->SetLabel(_("&Import"));
425 }
426 S.EndVerticalLay();
427
428 Fit();
429 SetSizeHints(GetSize());
430
431 Centre(wxBOTH);
432}
433
435{
436}
437
438void ImportRawDialog::OnOK(wxCommandEvent & WXUNUSED(event))
439{
440 long long l;
441
442 mEncoding = mEncodingSubtype[mEncodingChoice->GetSelection()];
443 mEncoding += (mEndianChoice->GetSelection() * 0x10000000);
444 mChannels = mChannelChoice->GetSelection() + 1;
445 mOffsetText->GetValue().ToLongLong(&l);
446 mOffset = l;
447 mPercentText->GetValue().ToDouble(&mPercent);
448 mRateText->GetValue().ToDouble(&mRate);
449
450 if (mChannels < 1 || mChannels > 16)
451 mChannels = 1;
452 if (mOffset < 0)
453 mOffset = 0;
454 if (mPercent < 0.0)
455 mPercent = 0.0;
456 if (mPercent > 100.0)
457 mPercent = 100.0;
458 if (mRate < 100.0)
459 mRate = 100.0;
460 // Highest preset sample rate supported in Audacity 2.3.0 is 384 kHz
461 if (mRate > 384000.0)
462 mRate = 384000.0;
463
464 EndModal(true);
465}
466
467void ImportRawDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
468{
469 EndModal(false);
470}
471
472void ImportRawDialog::OnPlay(wxCommandEvent & WXUNUSED(event))
473{
474}
475
476void ImportRawDialog::OnDetect(wxCommandEvent & event)
477{
478 try {
479 // Yes, FormatClassifier currently handles filenames in UTF8 format only, that's a TODO ...
480 FormatClassifier theClassifier(mFileName.utf8_str());
481 mEncoding = theClassifier.GetResultFormatLibSndfile();
482 mChannels = theClassifier.GetResultChannels();
483 } catch (...) {
484 // Something went wrong in FormatClassifier, abort.
485 return;
486 }
487
488 int selection = 0;
489 auto iter = std::find(mEncodingSubtype.begin(), mEncodingSubtype.end(), mEncoding & SF_FORMAT_SUBMASK);
490 if (iter != mEncodingSubtype.end()) // subtype found
491 selection = std::distance(mEncodingSubtype.begin(), iter);
492
493 int endian = getEndianChoice(mEncoding);
494
495 mEncodingChoice->SetSelection(selection);
496 mEndianChoice->SetSelection(endian);
497 mChannelChoice->SetSelection(mChannels - 1);
498
499 OnChoice(event);
500}
501
502void ImportRawDialog::OnChoice(wxCommandEvent & WXUNUSED(event))
503{
504 SF_INFO info;
505
506 memset(&info, 0, sizeof(SF_INFO));
507
508 mEncoding = mEncodingSubtype[mEncodingChoice->GetSelection()];
509 mEncoding += (mEndianChoice->GetSelection() * 0x10000000);
510
511 info.format = mEncoding | SF_FORMAT_RAW;
512 info.channels = mChannelChoice->GetSelection() + 1;
513 info.samplerate = 44100;
514
515 //mOK = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
516 if (sf_format_check(&info)) {
517 mOK->Enable(true);
518 return;
519 }
520
521 // Try it with 1-channel
522 info.channels = 1;
523 if (sf_format_check(&info)) {
524 mChannelChoice->SetSelection(0);
525 mOK->Enable(true);
526 return;
527 }
528
529 // Otherwise, this is an unsupported format
530 mOK->Enable(false);
531}
wxT("CloseDown"))
END_EVENT_TABLE()
#define str(a)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
unsigned int sf_encoding_index_to_subtype(int i)
Definition: FileFormats.cpp:90
wxString sf_encoding_index_name(int i)
Get the string name of the data encoding of the requested format.
Definition: FileFormats.cpp:79
sampleFormat sf_subtype_to_effective_format(unsigned int format)
Choose the narrowest value in the sampleFormat enumeration for a given libsndfile format.
int sf_num_encodings()
Get the number of data encodings libsndfile supports (in any container or none.
Definition: FileFormats.cpp:70
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
The interface that all file import "plugins" (if you want to call them that) must implement....
@ PlayID
Definition: ImportRaw.cpp:282
@ DetectID
Definition: ImportRaw.cpp:283
@ ChoiceID
Definition: ImportRaw.cpp:281
static int getEndianChoice(int sfFormat)
Definition: ImportRaw.cpp:260
void ImportRaw(const AudacityProject &project, wxWindow *parent, const wxString &fileName, WaveTrackFactory *trackFactory, TrackHolders &outTracks)
Definition: ImportRaw.cpp:106
std::vector< std::shared_ptr< Track > > TrackHolders
Definition: ImportRaw.h:24
#define _(s)
Definition: Internat.h:73
an object holding per-project preferred sample rate
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
constexpr sampleFormat int16Sample
Definition: SampleFormat.h:43
constexpr sampleFormat floatSample
Definition: SampleFormat.h:45
@ eIsCreating
Definition: ShuttleGui.h:37
const auto project
#define S(N)
Definition: ToChars.cpp:64
std::shared_ptr< TrackList > TrackListHolder
Definition: Track.h:42
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
An AudacityException with no visible message.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static const int StandardRates[]
Array of common audio sample rates.
Definition: AudioIOBase.h:248
static const int NumStandardRates
How many standard sample rates there are.
Definition: AudioIOBase.h:250
Thrown for failure of file or database operations in deeply nested places.
Definition: FileException.h:19
FormatClassifier classifies the sample format and endianness of raw audio files.
unsigned GetResultChannels()
Abstract base class used in importing a file.
ImportRawDialog prompts you with options such as endianness and sample size to help you importing dat...
Definition: ImportRaw.cpp:58
void OnCancel(wxCommandEvent &event)
Definition: ImportRaw.cpp:467
static long long mOffset
Definition: ImportRaw.cpp:74
wxChoice * mChannelChoice
Definition: ImportRaw.cpp:83
void OnOK(wxCommandEvent &event)
Definition: ImportRaw.cpp:438
void OnDetect(wxCommandEvent &event)
Definition: ImportRaw.cpp:476
wxButton * mOK
Definition: ImportRaw.cpp:80
static double mRate
Definition: ImportRaw.cpp:75
wxChoice * mEndianChoice
Definition: ImportRaw.cpp:82
static int mEncoding
Definition: ImportRaw.cpp:72
static unsigned mChannels
Definition: ImportRaw.cpp:73
wxTextCtrl * mOffsetText
Definition: ImportRaw.cpp:84
std::vector< int > mEncodingSubtype
Definition: ImportRaw.cpp:88
wxComboBox * mRateText
Definition: ImportRaw.cpp:86
static double mPercent
Definition: ImportRaw.cpp:76
void OnPlay(wxCommandEvent &event)
Definition: ImportRaw.cpp:472
ImportRawDialog(wxWindow *parent, const wxString &fileName)
Definition: ImportRaw.cpp:294
wxTextCtrl * mPercentText
Definition: ImportRaw.cpp:85
wxString mFileName
Definition: ImportRaw.cpp:90
wxChoice * mEncodingChoice
Definition: ImportRaw.cpp:81
void OnChoice(wxCommandEvent &event)
Definition: ImportRaw.cpp:502
static sampleFormat ChooseFormat(sampleFormat effectiveFormat)
Choose appropriate format, which will not be narrower than the specified one.
Definition: ImportUtils.cpp:19
static void ForEachChannel(TrackList &trackList, const std::function< void(WaveChannel &)> &op)
Iterates over channels in each wave track from the list.
Definition: ImportUtils.cpp:73
static void FinalizeImport(TrackHolders &outTracks, const std::vector< std::shared_ptr< WaveTrack > > &importedStreams)
Flushes the given channels and moves them to outTracks.
Definition: ImportUtils.cpp:49
ProgressDialog Class.
ProgressResult Update(int value, const TranslatableString &message={})
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
samplePtr ptr() const
Definition: SampleFormat.h:165
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
Can be thrown when user cancels operations, as with a progress dialog. Delayed handler does nothing.
Definition: UserException.h:17
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:871
TrackListHolder CreateMany(size_t nChannels)
Creates tracks with project's default rate and format and the given number of channels.
Definition: WaveTrack.cpp:423
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
const char * begin(const char *str) noexcept
Definition: StringUtils.h:101