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