sequencer
wave_reader.hpp
Go to the documentation of this file.
1 // Copied from https://github.com/adamstark/AudioFile
2 
3 #pragma once
4 
5 #include <fstream>
6 #include <iostream>
7 #include <iterator>
8 #include <string>
9 #include <vector>
10 
12 {
13  enum class endian_t
14  {
16  BigEndian
17  };
18 
19  inline std::int32_t four_bytes_to_int( const std::vector< uint8_t >& source,
20  std::vector< uint8_t >::size_type index,
21  endian_t endianness = endian_t::LittleEndian )
22  {
23  if ( endianness == endian_t::LittleEndian )
24  {
25  return ( source[ index + 3 ] << 24 ) | ( source[ index + 2 ] << 16 ) |
26  ( source[ index + 1 ] << 8 ) | source[ index ];
27  }
28 
29  return ( source[ index ] << 24 ) | ( source[ index + 1 ] << 16 ) |
30  ( source[ index + 2 ] << 8 ) | source[ index + 3 ];
31  }
32 
33  inline std::int16_t two_bytes_to_int( const std::vector< uint8_t >& source,
34  std::vector< uint8_t >::size_type index,
35  endian_t endianness = endian_t::LittleEndian )
36  {
37  if ( endianness == endian_t::LittleEndian )
38  {
39  return std::int16_t( source[ index + 1 ] << 8 ) | source[ index ];
40  }
41 
42  return std::int16_t( source[ index ] << 8 ) | source[ index + 1 ];
43  }
44 
45  inline int audio_file_format( const std::vector< std::uint8_t >& data )
46  {
47  std::string header( data.begin(), data.begin() + 4 );
48 
49  if ( header == "RIFF" )
50  {
51  return 1;
52  }
53  if ( header == "FORM" )
54  {
55  return 2;
56  }
57 
58  return 3;
59  }
60 
61  inline std::vector< uint8_t >::difference_type index_of( const std::vector< uint8_t >& source,
62  const std::string& str )
63  {
64  const auto length = str.length();
65 
66  for ( decltype( source.size() - length ) i = 0; i < source.size() - length; i++ )
67  {
68  using diff_t = std::vector< uint8_t >::difference_type;
69  std::string section( source.begin() + diff_t( i ),
70  source.begin() + diff_t( i + length ) );
71 
72  if ( section == str )
73  {
74  return diff_t( i );
75  }
76  }
77 
78  return -1;
79  }
80 
81  inline float byte_to_sample( uint8_t sample )
82  {
83  return float( sample - 128 ) / float( 128 );
84  }
85 
86  inline float two_bytes_to_sample( int16_t sample )
87  {
88  return float( sample ) / 32768.f;
89  }
90 
91  inline std::vector< std::vector< float > > read( const std::vector< std::uint8_t >& data )
92  {
93  const std::string header_chunk_id( data.begin(), data.begin() + 4 );
94  const std::string format( data.begin() + 8, data.begin() + 12 );
95 
96  const auto index_of_data_chunk = index_of( data, "data" );
97  const auto index_of_format_chunk = index_of( data, "fmt" );
98 
99  if ( index_of_data_chunk == -1 || index_of_format_chunk == -1 ||
100  header_chunk_id != "RIFF" || format != "WAVE" )
101  {
102  std::cerr << "ERROR: this doesn't seem to be a valid .WAV file" << std::endl;
103  return {};
104  }
105 
106  const auto f = index_of_format_chunk;
107  using size_t = std::vector< std::uint8_t >::size_type;
108  const std::string format_chunk_id( data.begin() + f, data.begin() + f + 4 );
109  const auto audio_format = two_bytes_to_int( data, size_t( f + 8 ) );
110  const auto channel_count = two_bytes_to_int( data, size_t( f + 10 ) );
111  const auto sample_rate = four_bytes_to_int( data, size_t( f + 12 ) );
112  const auto bytes_per_second = four_bytes_to_int( data, size_t( f + 16 ) );
113  const auto bytes_per_block = two_bytes_to_int( data, size_t( f + 20 ) );
114  const auto bit_depth = two_bytes_to_int( data, size_t( f + 22 ) );
115 
116  const int bytes_per_sample = bit_depth / 8;
117 
118  // check that the audio format is PCM
119  if ( audio_format != 1 )
120  {
121  std::cout << "ERROR: this is a compressed .WAV file and this library does not support "
122  "decoding them at present"
123  << std::endl;
124  return {};
125  }
126 
127  if ( channel_count < 1 || channel_count > 2 )
128  {
129  std::cout << "ERROR: this WAV file seems to be neither mono nor stereo (perhaps "
130  "multi-track, or corrupted?)"
131  << std::endl;
132  return {};
133  }
134 
135  if ( ( bytes_per_second != ( channel_count * sample_rate * bit_depth ) / 8 ) ||
136  ( bytes_per_block != ( channel_count * bytes_per_sample ) ) )
137  {
138  std::cout << "ERROR: the header data in this WAV file seems to be inconsistent"
139  << std::endl;
140  return {};
141  }
142 
143  if ( bit_depth != 8 && bit_depth != 16 && bit_depth != 24 )
144  {
145  std::cout << "ERROR: this file has a bit depth that is not 8, 16 or 24 bits"
146  << std::endl;
147  return {};
148  }
149 
150  const auto d = index_of_data_chunk;
151  const std::string data_chunk_id( data.begin() + d, data.begin() + d + 4 );
152  const auto data_chunk_size =
153  four_bytes_to_int( data, std::vector< std::uint8_t >::size_type( d + 4 ) );
154 
155  const auto num_samples = data_chunk_size / ( channel_count * bit_depth / 8 );
156  const auto sample_start_index = index_of_data_chunk + 8;
157 
158  std::vector< std::vector< float > > samples;
159  samples.resize( channel_count );
160 
161  for ( int i = 0; i < num_samples; i++ )
162  {
163  for ( int channel = 0; channel < channel_count; channel++ )
164  {
165  const auto sample_index =
166  sample_start_index + ( bytes_per_block * i ) + channel * bytes_per_sample;
167 
168  if ( bit_depth == 8 )
169  {
170  const auto sample = byte_to_sample( data[ sample_index ] );
171  samples[ channel ].push_back( sample );
172  }
173  else if ( bit_depth == 16 )
174  {
175  const auto sample_as_int = two_bytes_to_int( data, sample_index );
176  const auto sample = two_bytes_to_sample( sample_as_int );
177  samples[ channel ].push_back( sample );
178  }
179  else if ( bit_depth == 24 )
180  {
181  auto sample_as_int = 0;
182  sample_as_int = ( data[ sample_index + 2 ] << 16 ) |
183  ( data[ sample_index + 1 ] << 8 ) | data[ sample_index ];
184 
185  if ( ( sample_as_int & 0x800000 ) >
186  0 ) // if the 24th bit is set, this is a negative
187  { // number in 24-bit world
188  sample_as_int =
189  sample_as_int |
190  ~0xFFFFFF; // so make sure sign is extended to the 32 bit float
191  }
192 
193  const auto sample = sample_as_int / 8388608.f;
194  samples[ channel ].push_back( sample );
195  }
196  }
197  }
198 
199  return samples;
200  }
201 
202  inline std::vector< std::vector< float > > read( const std::string& filename )
203  {
204  std::ifstream file( filename, std::ios::binary );
205 
206  if ( !file.good() )
207  {
208  std::cerr << "ERROR: File doesn't exist or otherwise can't load file: " << filename
209  << std::endl;
210  return {};
211  }
212 
213  file.unsetf( std::ios::skipws );
214  std::istream_iterator< std::uint8_t > begin( file );
215  std::istream_iterator< std::uint8_t > end;
216  std::vector< std::uint8_t > data( begin, end );
217 
218  // get audio file format
219  const auto format = audio_file_format( data );
220  if ( format != 1 )
221  {
222  std::cerr << "wrong format: " << format << std::endl;
223  return {};
224  }
225 
226  return read( data );
227  }
228 } // namespace sequencer::audio::wave
int audio_file_format(const std::vector< std::uint8_t > &data)
Definition: wave_reader.hpp:45
std::int16_t two_bytes_to_int(const std::vector< uint8_t > &source, std::vector< uint8_t >::size_type index, endian_t endianness=endian_t::LittleEndian)
Definition: wave_reader.hpp:33
std::vector< uint8_t >::difference_type index_of(const std::vector< uint8_t > &source, const std::string &str)
Definition: wave_reader.hpp:61
endian_t
Definition: wave_reader.hpp:13
float two_bytes_to_sample(int16_t sample)
Definition: wave_reader.hpp:86
float byte_to_sample(uint8_t sample)
Definition: wave_reader.hpp:81
std::int32_t four_bytes_to_int(const std::vector< uint8_t > &source, std::vector< uint8_t >::size_type index, endian_t endianness=endian_t::LittleEndian)
Definition: wave_reader.hpp:19
std::vector< std::vector< float > > read(const std::vector< std::uint8_t > &data)
Definition: wave_reader.hpp:91
Definition: wave_reader.hpp:11