URY playd
C++ minimalist audio player
sndfile.cpp
Go to the documentation of this file.
1 // This file is part of playd.
2 // playd is licensed under the MIT licence: see LICENSE.txt.
3 
10 #include <cassert>
11 #include <cstdint>
12 #include <cstdlib>
13 #include <iostream>
14 #include <map>
15 #include <memory>
16 #include <sstream>
17 #include <string>
18 
19 #include <sndfile.h>
20 
21 #include "../../errors.hpp"
22 #include "../../messages.h"
23 #include "../sample_formats.hpp"
24 #include "../audio_source.hpp"
25 #include "sndfile.hpp"
26 
27 SndfileAudioSource::SndfileAudioSource(const std::string &path)
28  : AudioSource(path), file(nullptr), buffer()
29 {
30  this->info.format = 0;
31 
32  this->file = sf_open(path.c_str(), SFM_READ, &this->info);
33  if (this->file == nullptr) {
34  throw FileError("sndfile: can't open " + path + ": " +
35  sf_strerror(nullptr));
36  }
37 
38  // Reserve enough space for a given number of frames.
39  // (Frames being what libsndfile calls multi-channel samples, for some
40  // reason; incidentally, it calls mono-samples items.)
41  assert(0 < this->info.channels);
42  this->buffer.clear();
43  this->buffer.insert(this->buffer.begin(), 4096 * this->info.channels, 0);
44 }
45 
46 SndfileAudioSource::~SndfileAudioSource()
47 {
48  if (this->file != nullptr) sf_close(this->file);
49 }
50 
51 std::uint8_t SndfileAudioSource::ChannelCount() const
52 {
53  assert(0 < this->info.channels);
54  return static_cast<std::uint8_t>(this->info.channels);
55 }
56 
57 std::uint32_t SndfileAudioSource::SampleRate() const
58 {
59  assert(0 < this->info.samplerate);
60  // INT32_MAX isn't a typo; if we compare against UINT32_MAX, we'll
61  // set off sign-compare errors, and the sample rate shouldn't be above
62  // INT32_MAX anyroad.
63  assert(this->info.samplerate <= INT32_MAX);
64  return static_cast<std::uint32_t>(this->info.samplerate);
65 }
66 
67 std::uint64_t SndfileAudioSource::Seek(std::uint64_t in_samples)
68 {
69  // Have we tried to seek past the end of the file?
70  auto clen = static_cast<unsigned long>(this->info.frames);
71  if (clen < in_samples) {
72  Debug() << "sndfile: seek at" << in_samples << "past EOF at"
73  << clen << std::endl;
74  throw SeekError(MSG_SEEK_FAIL);
75  }
76 
77  auto out_samples = sf_seek(this->file, in_samples, SEEK_SET);
78  if (out_samples == -1) {
79  Debug() << "sndfile: seek failed" << std::endl;
80  throw SeekError(MSG_SEEK_FAIL);
81  }
82 
83  return out_samples;
84 }
85 
86 SndfileAudioSource::DecodeResult SndfileAudioSource::Decode()
87 {
88  auto read = sf_read_int(this->file, &*this->buffer.begin(),
89  this->buffer.size());
90 
91  // Have we hit the end of the file?
92  if (read == 0) {
93  return std::make_pair(DecodeState::END_OF_FILE, DecodeVector());
94  }
95 
96  // Else, we're good to go (hopefully).
97  //
98  // The buffer on our side is addressed as ints (32-bit), because this is
99  // easier for sndfile. However, the DecodeVector is addressed as bytes
100  // (8-bit) as the sample length could vary between files and decoders
101  // (from 8-bit up to 32-bit, and maybe even 32-bit float)!
102  //
103  // So, we reinterpret the decoded bits as a vector of bytes, which is
104  // relatively safe--they'll be interpreted by the AudioSink in the exact
105  // same way once we tell it how long the samples really are.
106  uint8_t *begin = reinterpret_cast<uint8_t *>(&*this->buffer.begin());
107 
108  // The end is 'read' 32-bit items--read*4 bytes--after.
109  uint8_t *end = begin + (read * 4);
110 
111  return std::make_pair(DecodeState::DECODING, DecodeVector(begin, end));
112 }
113 
114 SampleFormat SndfileAudioSource::OutputSampleFormat() const
115 {
116  // Because we use int-sized reads, assume this corresponds to 32-bit
117  // signed int.
118  // Really, we shouldn't assume int is 32-bit!
119  static_assert(sizeof(int) == 4,
120  "sndfile outputs int, which we need to be 4 bytes");
122 }
An object responsible for decoding an audio file.
An Error signifying that playd can&#39;t read a file.
Definition: errors.hpp:75
Packed 32-bit signed integer.
const std::string MSG_SEEK_FAIL
Message shown when an attempt to seek fails.
Definition: messages.h:95
An Error signifying that playd can&#39;t seek in a file.
Definition: errors.hpp:90
SampleFormat
Sample formats available in playd.
Class for telling the human what playd is doing.
Definition: errors.hpp:133
Declaration of the SndfileAudioSource class.