AudioHardware: Dispatch setParameters to input and output
[tinyalsa-audio.git] / AudioStreamOut.cpp
1 /*
2  * Copyright (C) 2012 Paul Kocialkowski <contact@paulk.fr>
3  *
4  * This is based on TinyHardware:
5  * Copyright 2011 Wolfson Microelectronics plc
6  *
7  * This is based on Nexus S AudioHardware implementation:
8  * Copyright 2010, The Android Open-Source Project
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22
23 #define LOG_TAG "AudioHardware"
24
25 #include "tinyalsa/asoundlib.h"
26
27 #include "AudioHardware.h"
28 #include "AudioStreamOut.h"
29 #include "Mixer.h"
30
31 namespace android {
32
33 TinyALSAAudioStreamOut::TinyALSAAudioStreamOut() :
34         mAudioHardware(NULL),
35         mMixer(NULL),
36         mPcm(NULL),
37         mDevice(0)
38 {
39         LOGD("TinyALSAAudioStreamOut()");
40
41         memset(&mPcmConfig, 0, sizeof(struct pcm_config));
42
43         mMixer = new TinyALSAMixer();
44 }
45
46 TinyALSAAudioStreamOut::~TinyALSAAudioStreamOut()
47 {
48         LOGD("~TinyALSAAudioStreamOut()");
49
50         if(mPcm)
51                 pcm_close(mPcm);
52
53         delete mMixer;
54 }
55
56 status_t TinyALSAAudioStreamOut::set(AudioHardware *hw, uint32_t devices,
57         int *pFormat, uint32_t *pChannels, uint32_t *pRate)
58 {
59         struct pcm *Pcm = NULL;
60         struct pcm_config PcmConfig;
61         int mode = AudioSystem::MODE_NORMAL;
62
63         LOGD("TinyALSAAudioStreamOut::set()");
64
65         // Setting globals
66         mAudioHardware = hw;
67         mDevice = devices;
68
69         // Setting PcmConfig
70         memset(&PcmConfig, 0, sizeof(struct pcm_config));
71         PcmConfig.channels = AudioSystem::popCount(*pChannels);
72         PcmConfig.rate = *pRate;
73         PcmConfig.format = PCM_FORMAT_S16_LE;
74         PcmConfig.period_size = 1056;
75         PcmConfig.period_count = 4;
76
77         Pcm = pcm_open(0, 0, PCM_OUT, &PcmConfig);
78
79         if(!Pcm || !pcm_is_ready(Pcm)) {
80                 LOGE("TinyALSAAudioStreamOut::set() failed: %s",
81                         pcm_get_error(Pcm));
82
83                 pcm_close(Pcm);
84
85                 LOGD("TinyALSAAudioStreamOut::set(): trying fixed params");
86
87                 // Fixed good known-to-work values
88                 PcmConfig.channels = 2;
89                 PcmConfig.rate = 44100;
90
91                 Pcm = pcm_open(0, 0, PCM_OUT, &PcmConfig);
92
93                 if(!Pcm || !pcm_is_ready(Pcm)) {
94                         LOGE("TinyALSAAudioStreamOut::set() failed: %s",
95                                 pcm_get_error(Pcm));
96
97                         return BAD_VALUE;
98                 }
99         }
100
101         // Do the routing
102         mDevice = devices;
103         mAudioHardware->getMode(&mode);
104         mMixer->route(mDevice, mode);
105
106         // Copy the PCM infos
107         mPcm = Pcm;
108         memcpy(&mPcmConfig, &PcmConfig, sizeof(struct pcm_config));
109
110         *pRate = sampleRate();
111         *pFormat = format();
112         *pChannels = channels();
113
114         return NO_ERROR;
115 }
116
117 uint32_t TinyALSAAudioStreamOut::sampleRate(void) const
118 {
119         return (uint32_t) mPcmConfig.rate;
120 }
121
122 size_t TinyALSAAudioStreamOut::bufferSize(void) const
123 {
124         return (size_t) pcm_get_buffer_size(mPcm);
125 }
126
127 uint32_t TinyALSAAudioStreamOut::channels(void) const
128 {
129         switch(mPcmConfig.channels) {
130                 case 1:
131                         return AudioSystem::CHANNEL_OUT_MONO;
132                 case 2:
133                         return AudioSystem::CHANNEL_OUT_STEREO;
134                 case 4:
135                         return AudioSystem::CHANNEL_OUT_QUAD;
136                 case 6:
137                         return AudioSystem::CHANNEL_OUT_5POINT1;
138                 default:
139                         return AudioSystem::CHANNEL_OUT_STEREO;
140         }
141 }
142
143 int TinyALSAAudioStreamOut::format(void) const
144 {
145         switch(mPcmConfig.format) {
146                 case PCM_FORMAT_S16_LE:
147                         return AudioSystem::PCM_16_BIT;
148                 default:
149                         return AudioSystem::PCM_16_BIT;
150         }
151 }
152
153 uint32_t TinyALSAAudioStreamOut::latency(void) const
154 {
155         return (mPcmConfig.period_size * mPcmConfig.period_count * 1000) / mPcmConfig.rate;
156 }
157
158 ssize_t TinyALSAAudioStreamOut::write(const void *buffer, size_t bytes)
159 {
160         AutoMutex lock(mLock);
161
162         if(!pcm_is_ready(mPcm)) {
163                 LOGE("TinyALSAAudioStreamOut::write() failed: PCM is not ready!");
164
165                 return 0;
166         }
167
168         if(pcm_write(mPcm, buffer, bytes) != 0) {
169                 LOGE("TinyALSAAudioStreamOut::write() failed: %s",
170                         pcm_get_error(mPcm));
171
172                 return 0;
173         }
174
175         return bytes;
176 }
177
178 status_t TinyALSAAudioStreamOut::standby()
179 {
180         if(pcm_stop(mPcm) != 0) {
181                 LOGE("TinyALSAAudioStreamOut::standby() failed: %s",
182                         pcm_get_error(mPcm));
183                 return BAD_VALUE;
184         }
185
186         return NO_ERROR;
187 }
188
189 status_t TinyALSAAudioStreamOut::setParameters(const String8& keyValuePairs)
190 {
191         AudioParameter param = AudioParameter(keyValuePairs);
192         String8 key = String8(AudioParameter::keyRouting);
193         int device;
194         int mode = AudioSystem::MODE_NORMAL;
195         float volume = 1;
196
197         LOGD("TinyALSAAudioStreamOut::setParameters(%s)",
198                 keyValuePairs.string());
199
200         if(param.getInt(key, device) == NO_ERROR) {
201                 mDevice = device;
202                 mAudioHardware->getMode(&mode);
203
204                 mMixer->route(mDevice, mode);
205
206                 mAudioHardware->getVoiceVolume(&volume);
207                 if(mode == AudioSystem::MODE_IN_CALL || mode == AudioSystem::MODE_IN_COMMUNICATION)
208                         mAudioHardware->setVoiceVolume(volume);
209
210                 if(mAudioHardware->mRILInterface)
211                         mAudioHardware->mRILInterface->setRouting(device);
212
213                 param.remove(key);
214         }
215
216         if(param.size()) {
217                 return BAD_VALUE;
218         }
219
220         return NO_ERROR;
221 }
222
223 String8 TinyALSAAudioStreamOut::getParameters(const String8& keys)
224 {
225         AudioParameter param = AudioParameter(keys);
226         String8 key = String8(AudioParameter::keyRouting);
227         String8 value;
228
229         LOGD("TinyALSAAudioStreamOut::getParameters()");
230
231         if(param.get(key, value) == NO_ERROR) {
232                 param.addInt(key, (int) mDevice);
233         }
234
235         return param.toString();
236 }
237
238 status_t TinyALSAAudioStreamOut::getRenderPosition(uint32_t *dspFrames)
239 {
240         return INVALID_OPERATION;
241 }
242
243
244 status_t TinyALSAAudioStreamOut::setVolume(float left, float right)
245 {
246         return INVALID_OPERATION;
247 }
248
249 status_t TinyALSAAudioStreamOut::dump(int fd, const Vector<String16>& args)
250 {
251         return NO_ERROR;
252 }
253
254 }; // namespace android