1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | #include <algorithm> |
11 | #include <map> |
12 | #include <sstream> |
13 | #include <stdexcept> |
14 | #include <string> |
15 | |
16 | extern "C" { |
17 | #include "libavformat/avformat.h" |
18 | #include "portaudio.h" |
19 | } |
20 | |
21 | #include "portaudiocpp/Device.hxx" |
22 | #include "portaudiocpp/DirectionSpecificStreamParameters.hxx" |
23 | #include "portaudiocpp/InterfaceCallbackStream.hxx" |
24 | #include "portaudiocpp/SampleDataFormat.hxx" |
25 | #include "portaudiocpp/Stream.hxx" |
26 | #include "portaudiocpp/StreamParameters.hxx" |
27 | #include "portaudiocpp/System.hxx" |
28 | #include "portaudiocpp/SystemDeviceIterator.hxx" |
29 | namespace portaudio { |
30 | class CallbackInterface; |
31 | } |
32 | |
33 | #include "../errors.hpp" |
34 | #include "../messages.h" |
35 | #include "../sample_formats.hpp" |
36 | #include "audio.hpp" |
37 | #include "audio_sink.hpp" |
38 | #include "audio_source.hpp" |
39 | #include "audio_system.hpp" |
40 | |
41 | AudioSystem::AudioSystem() |
42 | { |
43 | portaudio::System::initialize(); |
44 | av_register_all(); |
45 | |
46 | this->device_id = -1; |
47 | } |
48 | |
49 | AudioSystem::~AudioSystem() |
50 | { |
51 | portaudio::System::terminate(); |
52 | } |
53 | |
54 | std::vector<AudioSystem::Device> AudioSystem::GetDevicesInfo() |
55 | { |
56 | auto &pa = portaudio::System::instance(); |
57 | std::vector<AudioSystem::Device> list; |
58 | |
59 | for (auto d = pa.devicesBegin(); d != pa.devicesEnd(); ++d) { |
60 | if (!(*d).isInputOnlyDevice()) { |
61 | list.push_back(std::make_pair((*d).index(), (*d).name())); |
62 | } |
63 | } |
64 | return list; |
65 | } |
66 | |
67 | bool AudioSystem::IsOutputDevice(int id) |
68 | { |
69 | auto &pa = portaudio::System::instance(); |
70 | if (id < 0 || id >= pa.deviceCount()) return false; |
71 | |
72 | portaudio::Device &dev = pa.deviceByIndex(id); |
73 | return !dev.isInputOnlyDevice(); |
74 | } |
75 | |
76 | void AudioSystem::SetDeviceID(int id) |
77 | { |
78 | this->device_id = std::to_string(id); |
79 | } |
80 | |
81 | Audio *AudioSystem::Load(const std::string &path) const |
82 | { |
83 | auto source = new AudioSource(path); |
84 | |
85 | AudioSink::StreamConfigurator config_fn = std::bind( |
86 | &AudioSystem::Configure, this, source->ChannelCount(), |
87 | source->OutputSampleFormat(), source->SampleRate(), |
88 | source->BufferSampleCapacity(), std::placeholders::_1); |
89 | auto sink = new AudioSink(config_fn, source->BytesPerSample()); |
90 | |
91 | return new Audio(source, sink); |
92 | } |
93 | |
94 | portaudio::Stream *AudioSystem::Configure( |
95 | std::uint8_t channel_count, SampleFormat sample_format, |
96 | double sample_rate, size_t buffer_size, |
97 | portaudio::CallbackInterface &cb) const |
98 | { |
99 | const portaudio::Device &device = PaDeviceFrom(this->device_id); |
100 | |
101 | portaudio::DirectionSpecificStreamParameters out_pars( |
102 | device, channel_count, |
103 | PaSampleFormatFrom(sample_format), true, |
104 | device.defaultLowOutputLatency(), nullptr); |
105 | |
106 | portaudio::StreamParameters pars( |
107 | portaudio::DirectionSpecificStreamParameters::null(), |
108 | out_pars, sample_rate, buffer_size, paClipOff((PaStreamFlags) 0x00000001)); |
109 | |
110 | return new portaudio::InterfaceCallbackStream(pars, cb); |
111 | } |
112 | |
113 | const portaudio::Device &AudioSystem::PaDeviceFrom(const std::string &id_string) |
114 | const |
115 | { |
116 | auto &pa = portaudio::System::instance(); |
117 | |
118 | PaDeviceIndex id_pa = 0; |
119 | |
120 | std::istringstream is(id_string); |
121 | is >> id_pa; |
122 | |
123 | if (id_pa >= pa.deviceCount()) { |
124 | throw ConfigError(MSG_DEV_BADID); |
125 | } |
126 | |
127 | return pa.deviceByIndex(id_pa); |
128 | } |
129 | |
130 | |
131 | static const std::map<SampleFormat, portaudio::SampleDataFormat> pa_from_sf = { |
132 | { SampleFormat::PACKED_UNSIGNED_INT_8, portaudio::UINT8 }, |
133 | { SampleFormat::PACKED_SIGNED_INT_16, portaudio::INT16 }, |
134 | { SampleFormat::PACKED_SIGNED_INT_32, portaudio::INT32 }, |
135 | { SampleFormat::PACKED_FLOAT_32, portaudio::FLOAT32 } |
136 | }; |
137 | |
138 | portaudio::SampleDataFormat AudioSystem::PaSampleFormatFrom(SampleFormat fmt) |
139 | const |
140 | { |
141 | try |
| This statement is never executed |
142 | { |
143 | return pa_from_sf.at(fmt); |
144 | } |
145 | catch (std::out_of_range) |
146 | { |
147 | throw FileError(MSG_DECODE_BADRATE); |
148 | } |
149 | } |