SoundFileReaderMp3.hpp
1 #pragma once
2 
3 #include <cpp3ds/Audio/SoundFileReader.hpp>
4 #include <mpg123.h>
5 #include <string>
6 #include <cstdint>
7 
8 namespace cpp3ds
9 {
10 namespace priv
11 {
12 
13 class SoundFileReaderMp3 : public SoundFileReader
14 {
15 public:
16  SoundFileReaderMp3();
17 
18  ~SoundFileReaderMp3();
19 
20  // \brief Check if this reader can handle a file given by an input stream
21  // \param stream Source stream to check
22  // \return True if the file is supported by this reader
23  static bool check(InputStream& stream);
24 
25  // \brief Open a sound file for reading
26  // \param stream Source stream to read from
27  // \param info Structure to fill with the properties of the loaded sound
28  // \return True if the file was successfully opened
29  bool open(InputStream& stream, SoundFileReader::Info& info) override;
30 
31  // \brief Change the current read position to the given sample offset
32  // If the given offset exceeds to total number of samples,
33  // this function must jump to the end of the file.
34  // \param sampleOffset Index of the sample to jump to, relative to the beginning
35  void seek(Uint64 sampleOffset) override;
36 
37  // \brief Read audio samples from the open file
38  // \param samples Pointer to the sample array to fill
39  // \param maxCount Maximum number of samples to read
40  // \return Number of samples actually read (may be less than \a maxCount)
41  Uint64 read(Int16* samples, Uint64 maxCount) override;
42 
43 
44 protected:
45 
46  // Encapsulates mp3 output format
47  class OutputFormat {
48  private:
49  long m_rate = 0;
50  int m_channels = 0;
51  int m_encoding = 0;
52  public:
53  bool update (mpg123_handle* handle ){
54  return mpg123_getformat(handle, &m_rate, &m_channels, &m_encoding) == MPG123_OK;
55  }
56  std::string toString() const {
57  std::string result;
58 // result += "format: sample rate in Hz: " + std::to_string(m_rate);
59 // result += "\nformat: channels: " + std::to_string(m_channels);
60 // result += "\nformat: encoding: " + std::to_string(m_encoding);
61  return result;
62  }
63  };
64 
65  // Easy access to mpg123_frameinfo struct
66  class FrameInfo {
67  private:
68  mpg123_frameinfo m_info;
69  const std::size_t m_headerSize = 4;
70  public:
71  bool update (mpg123_handle* handle ){
72  return mpg123_info(handle, &m_info) == MPG123_OK;
73  }
74  std::size_t getFrameSizeInBytesIncludingHeader() const {
75  return m_info.framesize;
76  }
77  std::size_t getFrameSizeInBytesNoHeader() const {
78  return m_info.framesize - m_headerSize;
79  }
80  std::size_t getExpectedNumberOfSamples() const {
81  return getFrameSizeInBytesNoHeader() / sizeof(short);
82  }
83  std::uint32_t getSamplingRateInHz() const {
84  return m_info.rate;
85  }
86  std::uint32_t getNumberOfChannels() const {
87  switch (m_info.mode)
88  {
89  case mpg123_mode::MPG123_M_MONO:
90  return 1;
91  case mpg123_mode::MPG123_M_STEREO:
92  case mpg123_mode::MPG123_M_JOINT:
93  case mpg123_mode::MPG123_M_DUAL:
94  return 2;
95  default:
96  break;
97  }
98  return 0;
99  }
100  std::string toString() const {
101  std::string result;
102 // result += "header: MPEG version: ";
103 // switch (m_info.version)
104 // {
105 // case mpg123_version::MPG123_1_0:
106 // result += "1.0";
107 // break;
108 // case mpg123_version::MPG123_2_0:
109 // result += "2.0";
110 // break;
111 // case mpg123_version::MPG123_2_5:
112 // result += "2.5";
113 // break;
114 // default:
115 // result += "unknown version";
116 // break;
117 // }
118 // result += "\nheader: MPEG Audio mode: ";
119 // switch (m_info.mode) // Only the mono mode has 1 channel, the others have 2 channels.
120 // {
121 // case mpg123_mode::MPG123_M_STEREO:
122 // result += "Standard Stereo (2 channels)";
123 // break;
124 // case mpg123_mode::MPG123_M_JOINT:
125 // result += "Joint Stereo (2 channels)";
126 // break;
127 // case mpg123_mode::MPG123_M_DUAL:
128 // result += "Dual Channel (2 channels)";
129 // break;
130 // case mpg123_mode::MPG123_M_MONO:
131 // result += "Mono (single channel)";
132 // break;
133 // default:
134 // result += "unknown mode";
135 // break;
136 // }
137 // // enum mpg123_flags flags
138 // result += "\nheader: Mode type of variable bitrate (vbr): ";
139 // switch (m_info.vbr)
140 // {
141 // case mpg123_vbr::MPG123_CBR:
142 // result += "Constant Bitrate Mode (default)";
143 // break;
144 // case mpg123_vbr::MPG123_VBR:
145 // result += "Variable Bitrate Mode";
146 // break;
147 // case mpg123_vbr::MPG123_ABR:
148 // result += "Average Bitrate Mode";
149 // break;
150 // default:
151 // result += "unknown vbr mode";
152 // break;
153 // }
154 // result += "\nheader: MPEG Audio Layer (MP1/MP2/MP3): " + std::to_string(m_info.layer);
155 // result += "\nheader: Mode extension bit flag: " + std::to_string(m_info.mode_ext);
156 // result += "\nheader: Emphasis type (?): " + std::to_string(m_info.emphasis);
157 // result += "\nheader: Target average bitrate: " + std::to_string(m_info.abr_rate);
158 // result += "\nheader: Bitrate of the frame (kbps): " + std::to_string(m_info.bitrate);
159 // result += "\nheader: Sampling rate in Hz: " + std::to_string(m_info.rate);
160 // result += "\nheader: Size of the frame (in bytes, including header): " + std::to_string(m_info.framesize);
161 
162  return result;
163  }
164  };
165 
166  // library objects
167  mpg123_handle* m_handle;
168  InputStream* m_stream;
169  FrameInfo m_frameInfo;
170  OutputFormat m_outputFormat;
171 
172  // buffers
173  std::size_t m_streambufferSize;
174  unsigned char* m_streambuffer;
175 
176  // Creates the handle to the mp3 library
177  bool initializeLibrary();
178  // Used by open() to consume just enough data to retrieve frame info
179  // and output format so we can set parameters in SoundFileReader::Info
180  bool probeFirstFrame(InputStream& stream);
181  void fillSfmlInfo(SoundFileReader::Info& info) const;
182  // Called by destructor to close the library and delete the buffers
183  void close();
184 };
185 
186 } // namespace priv
187 
188 } // namespace cpp3ds
virtual Uint64 read(Int16 *samples, Uint64 maxCount)=0
Read audio samples from the open file.
virtual bool open(InputStream &stream, Info &info)=0
Open a sound file for reading.
virtual void seek(Uint64 sampleOffset)=0
Change the current read position to the given sample offset.