1 ///
2 /// LibAO output.
3 /// Audio output support for playing via libao.
4 ///	@file		libaooutput.cpp - pianod2
5 ///	@author		Perette Barella
6 ///	@date		2015-11-18
7 ///	@copyright	  Copyright (c) 2015-2016 Devious Fish. All rights reserved.
8 ///
9 
10 #include <config.h>
11 
12 #include <stdexcept>
13 
14 #include <errno.h>
15 
16 #include <ao/ao.h>
17 
18 #include "logging.h"
19 #include "audiooutput.h"
20 #include "libaooutput.h"
21 
22 using namespace std;
23 
24 namespace Audio {
25     /// Mutex to restrict concurrent calls into libao API.
26     std::mutex LibaoOutput::ao_mutex;
27 
28     /** Open audio output using libAO.
29         @param settings Device/ driver/ host to use for output.
30         @param data_format The format in which samples will arrive for output. */
31     LibaoOutput::LibaoOutput (const AudioSettings &settings,
32                               const AudioFormat &data_format) {
33 
34         ao_sample_format format = {};
35         format.bits = data_format.bits;
36         format.rate = data_format.rate;
37         format.channels = data_format.channels;
38         format.byte_format = (data_format.arrangement == SampleArrangement::Big ? AO_FMT_BIG :
39                               data_format.arrangement == SampleArrangement::Little ? AO_FMT_LITTLE :
40                               AO_FMT_NATIVE);
41 
42         // Find driver, or use default if unspecified.
43         int audio_out_driver = (settings.output_driver.empty()
44                                 ? ao_default_driver_id()
45                                 : ao_driver_id (settings.output_driver.c_str()));
46         if (audio_out_driver < 0) {
47             flog (LOG_WHERE (LOG_ERROR), "Audio driver ",
48                   settings.output_driver.empty() ? "(default)" : settings.output_driver.c_str(),
49                   " not found");
50             throw invalid_argument ("Invalid audio driver");
51         }
52 
53         // Create a list of ao_options
54         ao_option *options = nullptr;
55         ao_append_option(&options, "client_name", PACKAGE);
56         if (settings.output_device != "") {
57             ao_append_option (&options, "dev", settings.output_device.c_str());
58         }
59         if (settings.output_id != "") {
60             ao_append_option (&options, "id", settings.output_id.c_str());
61         }
62         if (settings.output_server != "") {
63             ao_append_option (&options, "server", settings.output_server.c_str());
64         }
65         {
66             lock_guard<mutex> lock (ao_mutex);
67             device = ao_open_live (audio_out_driver,
68                                    & const_cast <ao_sample_format &> (format),
69                                    options);
70         }
71         ao_free_options (options);
72         if (device == NULL) {
73             const char *reason = (errno == AO_ENODRIVER ? "No driver" :
74                                   errno == AO_ENOTLIVE ? "Not a live output device" :
75                                   errno == AO_EBADOPTION ? "Bad option" :
76                                   errno == AO_EOPENDEVICE ? "Cannot open device" : "Other failure");
77             flog (LOG_WHERE (LOG_ERROR), "Cannot open audio device ",
78                   settings.output_device.empty() ? "default" : settings.output_device.c_str(),
79                   "/",
80                   settings.output_id.empty() ? "default" : settings.output_id.c_str(),
81                   "/",
82                   settings.output_server.empty() ? "default" : settings.output_server.c_str(),
83                   ": ", reason);
84             throw AudioException (string (__func__) + ": " + reason);
85         }
86 
87         bytes_per_sample_set = data_format.sampleGroupSize();
88     }
89 
90     /** Play output.
91         @param buffer The samples, in packed (interleaved) format if multichannel.
92         @param number_of_bytes Size of the buffer; number of samples is determined
93         by the audio format set when opening the channel. */
94     bool LibaoOutput::play (void *buffer, unsigned number_of_bytes) {
95         lock_guard<mutex> lock (ao_mutex);
96         return ao_play(device, (char *) buffer, number_of_bytes);
97     };
98 
99     LibaoOutput::~LibaoOutput () {
100         lock_guard<mutex> lock (ao_mutex);
101         ao_close (device);
102     };
103 
104 }
105