Make wrapper for boost::lexical_cast
[lsnes.git] / src / platform / libao / sound.cpp
blobe3c9d8313b4452934d91159178c1f5a2ebb25431
1 #include "lsnes.hpp"
3 #include "cmdhelp/libao-sound.hpp"
4 #include "core/audioapi-driver.hpp"
5 #include "core/command.hpp"
6 #include "core/dispatch.hpp"
7 #include "core/framerate.hpp"
8 #include "core/misc.hpp"
9 #include "core/framerate.hpp"
10 #include "core/keymapper.hpp"
11 #include "core/settings.hpp"
12 #include "core/messages.hpp"
13 #include "core/window.hpp"
14 #include "library/framebuffer.hpp"
15 #include "library/string.hpp"
17 #include <cstring>
18 #include <cstdlib>
19 #include <vector>
20 #include <iostream>
21 #include <csignal>
22 #include <sstream>
23 #include <fstream>
24 #include <cassert>
25 #include "core/instance.hpp"
26 #include "library/minmax.hpp"
27 #include "library/workthread.hpp"
28 #include <string>
29 #include <map>
30 #include <stdexcept>
31 #include <ao/ao.h>
33 namespace
35 std::string current_device = "";
36 ao_device* volatile cdev = NULL;
37 bool init_flag = false;
38 bool was_enabled = false;
39 volatile bool do_quit_flag = false;
40 volatile bool do_quit_ack = false;
41 int driver_id = 0;
43 class cb_thread : public workthread
45 public:
46 cb_thread() { fire(); }
47 void entry()
49 int16_t buffer[1024];
50 while(!do_quit_flag) {
51 audioapi_get_mixed(buffer, 512, true);
52 if(!was_enabled)
53 memset(buffer, 0, sizeof(buffer));
54 ao_device* d = cdev;
55 if(d)
56 ao_play(d, reinterpret_cast<char*>(buffer), 2048);
57 else
58 usleep(10000);
60 do_quit_ack = true;
64 cb_thread* thread = NULL;
66 bool switch_devices(int newdevice, std::string name)
68 //If audio is open, close it.
69 if(cdev) {
70 ao_device* d = cdev;
71 cdev = NULL;
72 //Wait a bit for the device to close.
73 usleep(50000);
74 ao_close(d);
75 current_device = "";
76 audioapi_send_rate_change(0, 0);
78 //Open new audio.
79 if(newdevice != -1) {
80 char matrix[128];
81 strcpy(matrix, "L,R");
82 ao_sample_format sformat;
83 ao_option* options = NULL;
84 sformat.bits = 16;
85 sformat.rate = 48000;
86 sformat.channels = 2;
87 sformat.byte_format = AO_FMT_NATIVE;
88 sformat.matrix = matrix;
89 std::string idno = (stringfmt() << driver_id).str();
90 ao_append_option(&options, "id", idno.c_str());
91 cdev = ao_open_live(newdevice, &sformat, options);
92 if(!cdev) {
93 int err = errno;
94 //Error.
95 switch(err) {
96 case AO_ENODRIVER:
97 throw std::runtime_error("Bad driver ID");
98 case AO_ENOTLIVE:
99 throw std::runtime_error("Driver doesn't support playback");
100 case AO_EBADOPTION:
101 throw std::runtime_error("Bad option value");
102 case AO_EOPENDEVICE:
103 throw std::runtime_error("Can not open device");
104 case AO_EFAIL:
105 throw std::runtime_error("Unknown error");
106 default:
107 (stringfmt() << "Error code " << err).throwex();
110 audioapi_send_rate_change(0, 48000);
112 if(cdev) {
113 current_device = name;
114 messages << "Switched to audio output " << name << std::endl;
115 } else {
116 current_device = "";
117 messages << "Switched to null audio output" << std::endl;
119 return true;
122 command::fnptr<const std::string&> x(lsnes_cmds, CLIBAOSND::setid,
123 [](const std::string& value) throw(std::bad_alloc, std::runtime_error) {
124 driver_id = parse_value<int>(value);
127 struct _audioapi_driver drv = {
128 .init = []() -> void {
129 ao_initialize();
130 init_flag = true;
131 current_device = "";
133 int dcount;
134 int defaultid = ao_default_driver_id();
135 std::string dname = "";
136 ao_info** drvs = ao_driver_info_list(&dcount);
137 messages << "Detected " << dcount << " sound output devices." << std::endl;
138 for(int j = 0; j < dcount; j++) {
139 messages << "Audio device " << drvs[j]->short_name << ": " << drvs[j]->name
140 << std::endl;
141 if(j == defaultid)
142 dname = drvs[j]->short_name;
144 thread = new cb_thread;
145 was_enabled = true;
146 try {
147 switch_devices(defaultid, dname);
148 } catch(...) {
149 switch_devices(-1, "");
152 .quit = []() -> void {
153 if(!init_flag)
154 return;
155 switch_devices(-1, "");
156 ao_shutdown();
157 do_quit_flag = true;
158 while(!do_quit_ack)
159 usleep(10000);
160 do_quit_flag = false;
161 do_quit_ack = false;
162 //Threads are detecthed by default.
163 thread = NULL;
164 init_flag = false;
166 .enable = [](bool enable) -> void { was_enabled = enable; },
167 .initialized = []() -> bool { return init_flag; },
168 .set_device = [](const std::string& pdev, const std::string& rdev) -> void {
169 if(rdev != "null")
170 //Sound input not supported.
171 throw std::runtime_error("Invalid sound input device");
172 if(pdev == "null") {
173 if(!switch_devices(-1, ""))
174 throw std::runtime_error("Failed to switch sound outputs");
175 } else {
176 int idx = ao_driver_id(pdev.c_str());
177 if(idx == -1)
178 throw std::runtime_error("Invalid output device '" + pdev + "'");
179 try {
180 if(!switch_devices(idx, pdev))
181 throw std::runtime_error("Failed to switch sound outputs");
182 } catch(std::exception& e) {
183 throw std::runtime_error(std::string("Failed to switch sound outputs: ") +
184 e.what());
188 .get_device = [](bool rec) -> std::string {
189 if(rec)
190 return "null"; //Sound input not supported.
191 if(current_device == "")
192 return "null";
193 else
194 return current_device;
196 .get_devices = [](bool rec) -> std::map<std::string, std::string> {
197 std::map<std::string, std::string> ret;
198 if(rec) {
199 ret["null"] = "null sound input";
200 return ret;
202 ret["null"] = "null sound output";
203 int dcount;
204 ao_info** drvs = ao_driver_info_list(&dcount);
205 if(!drvs)
206 return ret;
207 for(int j = 0; j < dcount; j++)
208 if(drvs[j]->type == AO_TYPE_LIVE)
209 ret[drvs[j]->short_name] = drvs[j]->name;
210 return ret;
212 .name = []() -> const char* { return "Libao sound plugin"; }
214 audioapi_driver _drv(drv);