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