foobar2000 SDK  2015-08-03
writer_wav.cpp
Go to the documentation of this file.
1 #include "stdafx.h"
2 
3 
4 static const GUID guid_RIFF = pfc::GUID_from_text("66666972-912E-11CF-A5D6-28DB04C10000");
5 static const GUID guid_WAVE = pfc::GUID_from_text("65766177-ACF3-11D3-8CD1-00C04F8EDB8A");
6 static const GUID guid_FMT = pfc::GUID_from_text("20746D66-ACF3-11D3-8CD1-00C04F8EDB8A");
7 static const GUID guid_DATA = pfc::GUID_from_text("61746164-ACF3-11D3-8CD1-00C04F8EDB8A");
8 
11  const char * m_name;
12 };
13 
14 static const RIFF_chunk_desc RIFF_chunks[] = {
15  {guid_RIFF, "RIFF"},
16  {guid_WAVE, "WAVE"},
17  {guid_FMT , "fmt "},
18  {guid_DATA, "data"},
19 };
20 
22  if (this->m_bpsValid != this->m_bps) return true;
23  switch(m_channels)
24  {
25  case 1:
26  return m_channel_mask != audio_chunk::channel_config_mono;
27  case 2:
28  return m_channel_mask != audio_chunk::channel_config_stereo;
29 /* case 4:
30  m_wfxe = m_setup.m_channel_mask != (audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right);
31  break;
32  case 6:
33  m_wfxe = m_setup.m_channel_mask != audio_chunk::channel_config_5point1;
34  break;*/
35  default:
36  return true;
37  }
38 
39 }
40 
41 void wavWriterSetup_t::initialize3(const audio_chunk::spec_t & spec, unsigned bps, unsigned bpsValid, bool bFloat, bool bDither, bool bWave64) {
42  m_bps = bps;
43  m_bpsValid = bpsValid;
44  m_samplerate = spec.sampleRate;
45  m_channels = spec.chanCount;
46  m_channel_mask = spec.chanMask;
47  m_float = bFloat;
48  m_dither = bDither;
49  m_wave64 = bWave64;
50 }
51 
52 void wavWriterSetup_t::initialize2(const audio_chunk & p_chunk, unsigned p_bps, unsigned p_bpsValid, bool p_float, bool p_dither, bool p_wave64) {
53  m_bps = p_bps;
54  m_bpsValid = p_bpsValid;
55  m_samplerate = p_chunk.get_srate();
56  m_channels = p_chunk.get_channels();
57  m_channel_mask = p_chunk.get_channel_config();
58  m_float = p_float;
59  m_dither = p_dither;
60  m_wave64 = p_wave64;
61 }
62 
63 void wavWriterSetup_t::initialize(const audio_chunk & p_chunk, unsigned p_bps, bool p_float, bool p_dither, bool p_wave64)
64 {
65  unsigned bpsValid = p_bps;
66  unsigned bps = (p_bps + 7) & ~7;
67  initialize2(p_chunk, bps, bpsValid, p_float, p_dither, p_wave64);
68 }
69 
70 void wavWriterSetup_t::setup_wfx(WAVEFORMATEX & p_wfx)
71 {
72  p_wfx.wFormatTag = m_float ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
73  p_wfx.nChannels = m_channels;
74  p_wfx.nSamplesPerSec = m_samplerate;
75  p_wfx.nAvgBytesPerSec = (m_bps >> 3) * m_channels * m_samplerate;
76  p_wfx.nBlockAlign = (m_bps>>3) * m_channels;
77  p_wfx.wBitsPerSample = m_bps;
78  p_wfx.cbSize = 0;
79 }
80 
81 void wavWriterSetup_t::setup_wfxe(WAVEFORMATEXTENSIBLE & p_wfxe)
82 {
83  setup_wfx(p_wfxe.Format);
84  p_wfxe.Format.wFormatTag=WAVE_FORMAT_EXTENSIBLE;
85  p_wfxe.Format.cbSize=22;
86  p_wfxe.Samples.wValidBitsPerSample = this->m_bpsValid;
87  p_wfxe.dwChannelMask = audio_chunk::g_channel_config_to_wfx(m_channel_mask);
88  p_wfxe.SubFormat = m_float ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
89 
90 }
91 
92 void CWavWriter::writeID(const GUID & id, abort_callback & abort) {
93  if (is64()) {
94  m_file->write_object_t(id, abort);
95  } else {
96  for(t_size walk = 0; walk < PFC_TABSIZE(RIFF_chunks); ++walk) {
97  if (id == RIFF_chunks[walk].m_guid) {
98  m_file->write(RIFF_chunks[walk].m_name, 4, abort); return;
99  }
100  }
101  uBugCheck();
102  }
103 }
104 
106  if (is64()) {
107  if (size != ~0) size += 24;
108  m_file->write_lendian_t(size, abort);
109  } else {
110  t_uint32 clipped;
111  if (size > 0xFFFFFFFF) clipped = 0xFFFFFFFF;
112  else clipped = (t_uint32) size;
113  m_file->write_lendian_t(clipped, abort);
114  }
115 }
116 
118  t_uint8 dummy[8] = {};
119  const t_uint32 val = is64() ? 8 : 2;
120  t_filesize pos = m_file->get_position(abort);
121  t_size delta = (val - (pos%val)) % val;
122  if (delta > 0) m_file->write(dummy, delta, abort);
123  return delta;
124 }
125 
126 void CWavWriter::open(const char * p_path, const wavWriterSetup_t & p_setup, abort_callback & p_abort)
127 {
128  service_ptr_t<file> l_file;
129  filesystem::g_open_write_new(l_file,p_path,p_abort);
130  open(l_file,p_setup,p_abort);
131 }
132 
133 namespace {
134 PFC_DECLARE_EXCEPTION(exceptionBadBitDepth, exception_io_data, "Invalid bit depth specified");
135 }
136 void CWavWriter::open(service_ptr_t<file> p_file, const wavWriterSetup_t & p_setup, abort_callback & p_abort)
137 {
138  m_file = p_file;
139  m_setup = p_setup;
140 
141  if (m_setup.m_channels == 0 || m_setup.m_channels > 18 || m_setup.m_channels != audio_chunk::g_count_channels(m_setup.m_channel_mask)) throw exception_io_data();
142 
143  if (!audio_chunk::g_is_valid_sample_rate(m_setup.m_samplerate)) throw exception_io_data();
144 
145  if (m_setup.m_bpsValid > m_setup.m_bps) throw exceptionBadBitDepth();
146 
147  if (m_setup.m_float)
148  {
149  if (m_setup.m_bps != 32 && m_setup.m_bps != 64) throw exceptionBadBitDepth();
150  if (m_setup.m_bpsValid != m_setup.m_bps) throw exceptionBadBitDepth();
151  }
152  else
153  {
154  if (m_setup.m_bps != 8 && m_setup.m_bps != 16 && m_setup.m_bps != 24 && m_setup.m_bps != 32) throw exceptionBadBitDepth();
155  if (m_setup.m_bpsValid < 1) throw exceptionBadBitDepth();
156  }
157 
158  m_wfxe = m_setup.needWFXE();
159 
160  writeID(guid_RIFF, p_abort);
161  m_offset_fix1 = m_file->get_position(p_abort);
162  writeSize(~0, p_abort);
163 
164  writeID(guid_WAVE, p_abort);
165 
166  writeID(guid_FMT, p_abort);
167  if (m_wfxe) {
168  writeSize(sizeof(WAVEFORMATEXTENSIBLE),p_abort);
169 
170  WAVEFORMATEXTENSIBLE wfxe;
171  m_setup.setup_wfxe(wfxe);
172  m_file->write_object(&wfxe,sizeof(wfxe),p_abort);
173  } else {
174  writeSize(sizeof(PCMWAVEFORMAT),p_abort);
175 
176  WAVEFORMATEX wfx;
177  m_setup.setup_wfx(wfx);
178  m_file->write_object(&wfx,/* blah */ sizeof(PCMWAVEFORMAT),p_abort);
179  }
180  align(p_abort);
181 
182  writeID(guid_DATA, p_abort);
183  m_offset_fix2 = m_file->get_position(p_abort);
184  writeSize(~0, p_abort);
185  m_offset_fix1_delta = m_file->get_position(p_abort) - chunkOverhead();
186 
187 
188  m_bytes_written = 0;
189 
190  if (!m_setup.m_float)
191  {
192  m_postprocessor = standard_api_create_t<audio_postprocessor>();
193  }
194 }
195 
196 void CWavWriter::write(const audio_chunk & p_chunk, abort_callback & p_abort)
197 {
198  if (p_chunk.get_channels() != m_setup.m_channels
199  || p_chunk.get_channel_config() != m_setup.m_channel_mask
200  || p_chunk.get_srate() != m_setup.m_samplerate
201  ) throw exception_unexpected_audio_format_change();
202 
203 
204  if (m_setup.m_float)
205  {
206  switch(m_setup.m_bps)
207  {
208  case 32:
209  {
210 #if audio_sample_size == 32
211  t_size bytes = p_chunk.get_sample_count() * p_chunk.get_channels() * sizeof(audio_sample);
212  m_file->write_object(p_chunk.get_data(),bytes,p_abort);
213  m_bytes_written += bytes;
214 #else
215  enum {tempsize = 256};
216  float temp[tempsize];
217  t_size todo = p_chunk.get_sample_count() * p_chunk.get_channels();
218  const audio_sample * readptr = p_chunk.get_data();
219  while(todo > 0)
220  {
221  unsigned n,delta = todo;
222  if (delta > tempsize) delta = tempsize;
223  for(n=0;n<delta;n++)
224  temp[n] = (float)(*(readptr++));
225  unsigned bytes = delta * sizeof(float);
226  m_file->write_object_e(temp,bytes,p_abort);
227  m_bytes_written += bytes;
228  todo -= delta;
229  }
230 #endif
231  }
232  break;
233 #if 0
234  case 64:
235  {
236  unsigned bytes = p_chunk.get_sample_count() * p_chunk.get_channels() * sizeof(audio_sample);
237  m_file->write_object_e(p_chunk.get_data(),bytes,p_abort);
238  m_bytes_written += bytes;
239  }
240  break;
241 #endif
242  default:
243  throw exception_io_data();
244  }
245  }
246  else
247  {
248  m_postprocessor->run(p_chunk,m_postprocessor_output,m_setup.m_bpsValid,m_setup.m_bps,m_setup.m_dither,1.0f);
249  m_file->write_object(m_postprocessor_output.get_ptr(),m_postprocessor_output.get_size(),p_abort);
250  m_bytes_written += m_postprocessor_output.get_size();
251  }
252 }
253 
255 {
256  if (m_file.is_valid())
257  {
258  const size_t alignG = align(p_abort);
259 
260  if (m_file->can_seek()) {
261  m_file->seek(m_offset_fix1,p_abort);
262  writeSize(m_bytes_written + alignG + m_offset_fix1_delta, p_abort);
263  m_file->seek(m_offset_fix2,p_abort);
264  writeSize(m_bytes_written, p_abort);
265  }
266  m_file.release();
267  }
268  m_postprocessor.release();
269 }
270 
272 {
273  m_file.release();
274  m_postprocessor.release();
275 }
276 
278  audio_chunk::spec_t spec = {};
279  spec.sampleRate = m_setup.m_samplerate;
280  spec.chanCount = m_setup.m_channels;
281  spec.chanMask = m_setup.m_channel_mask;
282  return spec;
283 }
void writeID(const GUID &id, abort_callback &abort)
Definition: writer_wav.cpp:92
uint8_t t_uint8
Definition: int_types.h:9
void setup_wfx(WAVEFORMATEX &p_wfx)
Definition: writer_wav.cpp:70
Definition: pfc.h:71
uint64_t t_uint64
Definition: int_types.h:3
void setup_wfxe(WAVEFORMATEXTENSIBLE &p_wfx)
Definition: writer_wav.cpp:81
static const GUID guid_WAVE
Definition: writer_wav.cpp:5
Interface to container of a chunk of audio data. See audio_chunk_impl for an implementation.
Definition: audio_chunk.h:5
GUID GUID_from_text(const char *text)
Definition: guid.cpp:106
virtual unsigned get_channels() const =0
Retrieves channel count of contained audio data.
void write(const audio_chunk &p_chunk, abort_callback &p_abort)
Definition: writer_wav.cpp:196
static const GUID guid_FMT
Definition: writer_wav.cpp:6
void initialize(const audio_chunk &p_chunk, unsigned p_bps, bool p_float, bool p_dither, bool p_wave64=false)
Definition: writer_wav.cpp:63
virtual unsigned get_srate() const =0
Retrieves sample rate of contained audio data.
virtual unsigned get_channel_config() const =0
Retrieves channel map of contained audio data. Conditions where number of channels specified by chann...
static unsigned g_count_channels(unsigned p_config)
Counts channels specified by channel map.
size_t align(abort_callback &abort)
Definition: writer_wav.cpp:117
size_t t_size
Definition: int_types.h:48
void open(const char *p_path, const wavWriterSetup_t &p_setup, abort_callback &p_abort)
Definition: writer_wav.cpp:126
virtual audio_sample * get_data()=0
Retrieves audio data buffer pointer (non-const version). Returned pointer is for temporary use only; ...
audio_chunk::spec_t get_spec() const
Definition: writer_wav.cpp:277
static const GUID guid_DATA
Definition: writer_wav.cpp:7
PFC_DECLARE_EXCEPTION(exception_bad_cuesheet, exception_io_data,"Invalid cuesheet")
static const GUID guid_RIFF
Definition: writer_wav.cpp:4
void initialize2(const audio_chunk &p_chunk, unsigned p_bps, unsigned p_bpsValid, bool p_float, bool p_dither, bool p_wave64=false)
Definition: writer_wav.cpp:52
float audio_sample
Definition: audio_sample.h:6
void finalize(abort_callback &p_abort)
Definition: writer_wav.cpp:254
t_uint64 t_filesize
Type used for file size related variables.
Definition: filesystem.h:8
void close()
Definition: writer_wav.cpp:271
void initialize3(const audio_chunk::spec_t &spec, unsigned bps, unsigned bpsValid, bool bFloat, bool bDither, bool bWave64=false)
Definition: writer_wav.cpp:41
void writeSize(t_uint64 size, abort_callback &abort)
Definition: writer_wav.cpp:105
bool needWFXE() const
Definition: writer_wav.cpp:21
static bool g_is_valid_sample_rate(t_uint32 p_val)
Definition: audio_chunk.h:11
const char * m_name
Definition: writer_wav.cpp:11
PFC_NORETURN void SHARED_EXPORT uBugCheck()
static const RIFF_chunk_desc RIFF_chunks[]
Definition: writer_wav.cpp:14
virtual t_size get_sample_count() const =0
Retrieves number of valid samples in the buffer. Note that a "sample" means a unit of interleaved PC...
uint32_t t_uint32
Definition: int_types.h:5
static uint32_t g_channel_config_to_wfx(unsigned p_config)
Helper function; translates audio_chunk channel map to WAVEFORMATEXTENSIBLE channel map...