Merge pull request #40 from McSinyx/travis
[alure.git] / examples / alure-hrtf.cpp
blobb658cdb7927da2d2143af5ca80e91bcf48816b5f
1 /*
2 * An example showing how to enable HRTF rendering, using the ALC_SOFT_HRTF
3 * extension.
4 */
6 #include <algorithm>
7 #include <iostream>
8 #include <iomanip>
9 #include <cstring>
10 #include <thread>
11 #include <chrono>
13 #include "alure2.h"
16 namespace {
18 // Helper class+method to print the time with human-readable formatting.
19 struct PrettyTime {
20 alure::Seconds mTime;
22 inline std::ostream &operator<<(std::ostream &os, const PrettyTime &rhs)
24 using hours = std::chrono::hours;
25 using minutes = std::chrono::minutes;
26 using seconds = std::chrono::seconds;
27 using centiseconds = std::chrono::duration<int64_t, std::ratio<1, 100>>;
28 using std::chrono::duration_cast;
30 centiseconds t = duration_cast<centiseconds>(rhs.mTime);
31 if(t.count() < 0)
33 os << '-';
34 t *= -1;
37 // Only handle up to hour formatting
38 if(t >= hours(1))
39 os << duration_cast<hours>(t).count() << 'h' << std::setfill('0') << std::setw(2)
40 << duration_cast<minutes>(t).count() << 'm';
41 else
42 os << duration_cast<minutes>(t).count() << 'm' << std::setfill('0');
43 os << std::setw(2) << (duration_cast<seconds>(t).count() % 60) << '.' << std::setw(2)
44 << (t.count() % 100) << 's' << std::setw(0) << std::setfill(' ');
45 return os;
48 } // namespace
50 int main(int argc, char *argv[])
52 alure::ArrayView<const char*> args(argv, argc);
54 if(args.size() < 2)
56 std::cerr<< "Usage: "<<args.front()<<" [-device \"device name\"] [-hrtf \"HRTF name\"] files..." <<std::endl;
57 return 1;
59 args = args.slice(1);
61 alure::DeviceManager devMgr = alure::DeviceManager::getInstance();
63 alure::Device dev;
64 if(args.size() > 2 && args[0] == alure::StringView("-device"))
66 dev = devMgr.openPlayback(args[1], std::nothrow);
67 if(!dev) std::cerr<< "Failed to open \""<<args[1]<<"\" - trying default" <<std::endl;
68 args = args.slice(2);
70 if(!dev) dev = devMgr.openPlayback();
71 std::cout<< "Opened \""<<dev.getName()<<"\"" <<std::endl;
73 // Enumerate (and display) the available HRTFs
74 alure::Vector<alure::String> hrtf_names = dev.enumerateHRTFNames();
75 if(hrtf_names.empty())
76 std::cout<< "No HRTFs found!\n";
77 else
79 std::cout<< "Available HRTFs:\n";
80 for(const alure::String &name : hrtf_names)
81 std::cout<< " "<<name <<'\n';
83 std::cout.flush();
85 alure::Vector<alure::AttributePair> attrs;
86 attrs.push_back({ALC_HRTF_SOFT, ALC_TRUE});
87 if(args.size() > 1 && alure::StringView("-hrtf") == args[0])
89 // Find the given HRTF and add it to the attributes list
90 alure::StringView hrtf_name = args[1];
91 args = args.slice(2);
93 size_t idx = std::distance(
94 hrtf_names.begin(), std::find(hrtf_names.begin(), hrtf_names.end(), hrtf_name)
96 if(idx == hrtf_names.size())
97 std::cerr<< "HRTF \""<<hrtf_name<<"\" not found" <<std::endl;
98 else
99 attrs.push_back({ALC_HRTF_ID_SOFT, static_cast<ALint>(idx)});
101 attrs.push_back(alure::AttributesEnd());
102 alure::Context ctx = dev.createContext(attrs);
103 alure::Context::MakeCurrent(ctx);
105 if(dev.isHRTFEnabled())
106 std::cout<< "Using HRTF \""<<dev.getCurrentHRTF()<<"\"" <<std::endl;
107 else
108 std::cout<< "HRTF not enabled!" <<std::endl;
110 for(;!args.empty();args = args.slice(1))
112 if(args.size() > 1 && alure::StringView("-hrtf") == args[0])
114 // Find the given HRTF and reset the device using it
115 alure::StringView hrtf_name = args[1];
116 size_t idx = std::distance(
117 hrtf_names.begin(), std::find(hrtf_names.begin(), hrtf_names.end(), hrtf_name)
119 if(idx == hrtf_names.size())
120 std::cerr<< "HRTF \""<<hrtf_name<<"\" not found" <<std::endl;
121 else
123 alure::Array<alure::AttributePair,3> attrs{{
124 {ALC_HRTF_SOFT, ALC_TRUE},
125 {ALC_HRTF_ID_SOFT, static_cast<ALint>(idx)},
126 alure::AttributesEnd()
128 dev.reset(attrs);
129 if(dev.isHRTFEnabled())
130 std::cout<< "Using HRTF \""<<dev.getCurrentHRTF()<<"\"" <<std::endl;
131 else
132 std::cout<< "HRTF not enabled!" <<std::endl;
135 args = args.slice(1);
136 continue;
139 alure::SharedPtr<alure::Decoder> decoder = ctx.createDecoder(args.front());
140 alure::Source source = ctx.createSource();
142 source.play(decoder, 12000, 4);
143 std::cout<< "Playing "<<args.front()<<" ("
144 << alure::GetSampleTypeName(decoder->getSampleType())<<", "
145 << alure::GetChannelConfigName(decoder->getChannelConfig())<<", "
146 << decoder->getFrequency()<<"hz)" <<std::endl;
148 double invfreq = 1.0 / decoder->getFrequency();
149 while(source.isPlaying())
151 std::cout<< "\r "<<PrettyTime{source.getSecOffset()}<<" / "<<
152 PrettyTime{alure::Seconds(decoder->getLength()*invfreq)};
153 std::cout.flush();
154 std::this_thread::sleep_for(std::chrono::milliseconds(25));
155 ctx.update();
157 std::cout<<std::endl;
159 source.destroy();
162 alure::Context::MakeCurrent(nullptr);
163 ctx.destroy();
164 dev.close();
166 return 0;