blob: 307425fbd329bde369dc2975a54fd48181af15ac [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package voice provides functionality related to voice inputs
package voice
import (
"context"
"time"
"chromiumos/tast/ctxutil"
"chromiumos/tast/errors"
"chromiumos/tast/local/audio"
"chromiumos/tast/local/audio/crastestclient"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/uiauto"
"chromiumos/tast/local/chrome/uiauto/nodewith"
"chromiumos/tast/local/chrome/uiauto/quicksettings"
"chromiumos/tast/local/chrome/uiauto/role"
)
// AudioFromFile inputs an audio file via active input node and waits for its completion.
func AudioFromFile(ctx context.Context, audioFilePath string) error {
audioInput := audio.TestRawData{
Path: audioFilePath,
BitsPerSample: 16,
Channels: 2,
Rate: 48000,
Frequencies: []int{440, 440},
Volume: 0.05,
}
// Playback function by CRAS.
playCmd := crastestclient.PlaybackFileCommand(
ctx, audioInput.Path,
audioInput.Duration,
audioInput.Channels,
audioInput.Rate)
playCmd.Start()
return playCmd.Wait()
}
// EnableAloop loads and enables Aloop then sets it as active input/output node.
func EnableAloop(ctx context.Context, tconn *chrome.TestConn) (func(ctx context.Context), error) {
// Load ALSA loopback module.
unload, err := audio.LoadAloop(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to load ALSA loopback module")
}
// Activate the Aloop nodes.
if err := activateAloopNodes(ctx, tconn); err != nil {
// Unload ALSA loopback if any following setups failed.
unload(ctx)
return nil, err
}
return unload, nil
}
// activateAloopNodes activates Aloop nodes as input/output devices.
// Switching nodes via UI interactions is the recommended way, instead of using
// cras.SetActiveNode() method, as UI will always send the preference input/output
// devices to CRAS. Calling cras.SetActiveNode() changes the active devices for a
// moment, but they soon are reverted by UI. See (b/191602192) for details.
func activateAloopNodes(ctx context.Context, tconn *chrome.TestConn) error {
cleanupCtx, shortCancel := ctxutil.Shorten(ctx, 2*time.Second)
defer shortCancel()
if err := quicksettings.Show(ctx, tconn); err != nil {
return errors.Wrap(err, "failed to show Quick Settings")
}
defer quicksettings.Hide(cleanupCtx, tconn)
if err := selectAudioOption(ctx, tconn, "Loopback Playback"); err != nil {
return errors.Wrap(err, "failed to select ALSA loopback output")
}
if err := selectAudioOption(ctx, tconn, "Loopback Capture"); err != nil {
return errors.Wrap(err, "failed to select ALSA loopback input")
}
return nil
}
func selectAudioOption(ctx context.Context, tconn *chrome.TestConn, device string) error {
if err := quicksettings.OpenAudioSettings(ctx, tconn); err != nil {
return err
}
ui := uiauto.New(tconn)
option := nodewith.Role(role.CheckBox).Name(device)
if err := ui.DoDefault(option)(ctx); err != nil {
return errors.Wrapf(err, "failed to click %v audio option", device)
}
return nil
}