Kiểm soát âm thanh trên web với React và Redux Middleware


Võ Thạch Thảo
1 năm trước
Hữu ích 1 Chia sẻ Viết bình luận 0
Đã xem 3882

Hãy xây dựng Bàn phím TouchTone!

Nếu bạn đã xây dựng các ứng dụng React / Redux trước đó, bạn sẽ biết có một mẫu chuẩn của luồng dữ liệu theo hướng. UI gửi một hành động. Một bộ giảm xử lý hành động, trả về trạng thái ứng dụng mới. Giao diện người dùng tự tổ chức lại cho phù hợp.

Nhưng nếu bạn cần một hành động Redux để kích hoạt tương tác với một hệ thống phức tạp thì sao? Nói, một bộ sưu tập các thành phần Web Audio được sử dụng để tạo hoặc phân tích âm thanh. Đó không phải là đối tượng tuần tự hóa. Họ không nên được quản lý bởi một bộ giảm tốc. Thành phần UI cũng không nên quản lý chúng vì nó có thể bị ngắt kết nối khi chạy, gây mất hệ thống âm thanh.

Thay vào đó, chúng tôi sử dụng phần mềm trung gian, và đó là trọng tâm của bài viết này.

Middleware tồn tại lâu dài và do đó có thể được dự kiến ​​sẽ duy trì bất kỳ hệ thống âm thanh nào chúng tôi xây dựng cho vòng đời của ứng dụng. Hơn nữa, nó có quyền truy cập vào cửa hàng, do đó, nó có thể kiểm tra trạng thái ứng dụng, gửi các hành động để phản ứng với các sự kiện từ điện tích của nó, ví dụ, hệ thống Web Audio và có thể phản hồi các hành động hướng nó tương tác với hệ thống đó.

Trong bản demo này, chúng tôi sẽ giữ cho nó đơn giản và chỉ kích hoạt một số âm thanh để đáp ứng với một hành động. Mục tiêu chỉ đơn giản là trình diễn cách sử dụng phần mềm trung gian để điều chỉnh hệ thống API Web âm thanh cho ứng dụng Redux.

Để kỷ niệm sự kiện tôi vừa bỏ tháng này sau 12 năm, (chuyển số sang  dịch vụ trực tuyến openphone.co tuyệt vời  ), chúng tôi sẽ mô phỏng bàn phím điện thoại TouchTone.

Nghiên cứu miền vấn đề

Điện thoại TouchTone sử dụng một hệ thống gọi là   tín hiệu DTMF (Đa tần số âm kép), kích hoạt hai tần số riêng biệt khi nhấn phím.

Bài viết trên Wikipedia được liên kết chứa nhiều câu đố hơn, giống như thực tế là biểu tượng '#' được các kỹ sư ban đầu gọi là octothorpe . Nhưng, đối với hầu hết các phần, tất cả những gì chúng ta cần biết được chứa trong một bảng bạn thấy ở đây.

Trong ứng dụng của chúng tôi, hai bộ tạo dao động trong một hệ thống API Web âm thanh nhỏ có thể tạo ra các tần số đó và chúng tôi sẽ sử dụng chức năng phần mềm trung gian Redux để hoạt động như một trung gian.

Xây dựng ứng dụng

Đầu tiên, chúng ta chỉ cần mã hóa bảng tần số ma thuật này theo cách mà chúng ta có thể dễ dàng tạo ra một bàn phím sử dụng thông tin đó.

Phương pháp vũ lực sẽ là khai báo riêng từng nút và mã hóa tần số thích hợp trong trình xử lý nhấp chuột của mỗi nút. Tuy nhiên, cách tối ưu sẽ là sắp xếp dữ liệu để chúng ta thực sự có thể tạo bàn phím từ nó. Không cần phải nói, thứ hai là lựa chọn chúng ta sẽ sử dụng ở đây.

Đại diện cho tên miền

Trước tiên, chúng tôi xác định tần số hàng và tần số cột, sau đó tạo một mảng cho mỗi khóa với các hằng số tần số cho vị trí hàng và cột của nó. Cuối cùng, một mảng các mảng biểu thị các hàng bàn phím được xây dựng, với mỗi phím được biểu thị bằng một mảng chứa nhãn và mảng âm cho phím đó.

dtmf.js

// DTMF ROW FREQUENCIES
const ROW_1 = 697;
const ROW_2 = 770;
const ROW_3 = 852;
const ROW_4 = 941;

// DTMF COLUMN FREQUENCIES
const COL_1 = 1209;
const COL_2 = 1336;
const COL_3 = 1477;

// DTMF KEY FREQUENCY PAIRS
const KEY_1 = [ROW_1, COL_1];
const KEY_2 = [ROW_1, COL_2];
const KEY_3 = [ROW_1, COL_3];

const KEY_4 = [ROW_2, COL_1];
const KEY_5 = [ROW_2, COL_2];
const KEY_6 = [ROW_2, COL_3];

const KEY_7 = [ROW_3, COL_1];
const KEY_8 = [ROW_3, COL_2];
const KEY_9 = [ROW_3, COL_3];

const KEY_STAR  = [ROW_4, COL_1];
const KEY_0     = [ROW_4, COL_2];
const KEY_POUND = [ROW_4, COL_3];

// DTMF KEYPAD LABELS AND FREQUENCY PAIRS
export const KEYPAD = [
    [ ['1', KEY_1],    ['2', KEY_2], ['3', KEY_3], ],   // KEYPAD ROW 1
    [ ['4', KEY_4],    ['5', KEY_5], ['6', KEY_6], ],   // KEYPAD ROW 2
    [ ['7', KEY_7],    ['8', KEY_8], ['9', KEY_9], ],   // KEYPAD ROW 3
    [ ['*', KEY_STAR], ['0', KEY_0], ['#', KEY_POUND] ] // KEYPAD ROW 4
];

Giao diện người dùng đến tin nhắn Middleware

Trước khi chúng tôi truy cập vào giao diện người dùng để đại diện cho bàn phím hoặc phần mềm trung gian để phát âm DTMF, chúng ta hãy xem nhanh thông báo sẽ được gửi giữa hai.

Người tạo hành động  playDTMFPair  sẽ chấp nhận một cặp âm như được xác định trong các  KEY_  hằng số ở trên và trả về một hành động loại  PLAY_DTMF_PAIR mà qua đó các âm có thể được gửi từ giao diện người dùng đến phần mềm trung gian mỗi lần nhấn phím.

Action.js

// Audio related actions
export const PLAY_DTMF_PAIR = 'audio/play-dtmf';

// Play a DTMF tone pair
export const playDTMFPair = tones => {
    return {
        type: PLAY_DTMF_PAIR,
        tones
    };
};

Tạo giao diện người dùng

Bản demo là một thiết lập React / Redux tiêu chuẩn. Thêm vào đó, nó sử dụng  Phản ứng Bootstrap  và  Linh kiện theo kiểu  để đạt được cái nhìn và cảm nhận của một bàn phím touchtone điển hình với lớn, hình vuông, các nút bóng mờ sắp xếp thành hàng chặt chẽ. Bạn có thể xem lại  mã dự án  cho các khía cạnh kiểu dáng, nhưng dưới đây là hai thành phần React chính được sử dụng để kết xuất bàn phím.

Các  App thành phần, như thường lệ, container chính. Trong render phương pháp của nó  , nó tạo ra một  cơ bản là một flexbox cột với định tâm nội dung và một số lề trên. Bên trong đó, nó kết xuất một  thùng chứa cho mỗi hàng của bàn phím (bạn đoán nó, một hộp linh hoạt hướng hàng). Cuối cùng, bên trong mỗi cái đó, nó hiển thị một  thành phần cho mọi khóa trong hàng, chuyển nhãn, âm mà phím cần kích hoạt và bộ điều phối cho hành động. StyledKeypadStyledKeypadRow KeypadKey 

App.js

import React, {Component} from 'react';
import {connect} from 'react-redux';

import {KEYPAD} from '../constants/dtmf';
import {playDTMFPair} from '../store/audio/actions';
import {StyledKeypad} from '../styles/StyledKeypad';
import {StyledKeypadRow} from '../styles/StyledKeypadRow';
import KeypadKey from './KeypadKey';

class App extends Component {

    render() {

        const {playTones} = this.props;

        return <StyledKeypad>
            {
                KEYPAD.map( (row, rindex) =>
                <StyledKeypadRow key={rindex}>
                    {row.map( key => <KeypadKey
                        key={key[0]}
                        label={key[0]}
                        tones={key[1]}
                        handleClick={playTones}/>)}
                </StyledKeypadRow>)
            }
        </StyledKeypad>;
    }
}

const mapDispatchToProps = (dispatch) => ({
    playTones: tones => dispatch(playDTMFPair(tones))
});

export default connect(null, mapDispatchToProps)(App);

Các  KeypadKey thành phần là một thành phần chức năng đơn giản mà chấp nhận  labeltonesvà  handleClick chức năng chúng tôi vượt qua như đạo cụ. Nó trả về một  StyledKeypadButton, đó chỉ là một nút Bootstrap vuông lớn không có đường viền, kích thước phông chữ có thể đọc được và  onClick trình xử lý gọi hàm, truyền qua   mảng. handleClick tones

Bàn phím phím

import React from 'react';

import {StyledKeypadButton} from '../styles/StyledKeypadButton';

export default function KeypadKey(props) {

    const {label, tones, handleClick} = props;

    return <StyledKeypadButton onClick={() => handleClick(tones)}>{label}</StyledKeypadButton>

}

API âm thanh trên web

Các trình tổng hợp đa âm hoàn chỉnh   đã được xây dựng bằng API Web âm thanh, đó là chiều rộng tuyệt vời khi thực hiện. Và chọn React / Redux cho khung ứng dụng tổng thể của bạn là một cách tuyệt vời để bắt đầu một dự án như vậy. Mặc dù bản demo của chúng tôi sẽ được so sánh, kiến ​​trúc có thể dễ dàng được điều chỉnh theo mục đích lớn như vậy.

Nếu bạn chưa quen với nó, một phần  giới thiệu tuyệt vời  về API Web âm thanh có sẵn tại các thủ thuật css. Trong thực tế,  một phần  của nó tạo thành cơ sở cho TouchTone lớp học của chúng tôi  . Sự khác biệt chính là chúng ta lấy bối cảnh âm thanh trong hàm tạo thay vì chấp nhận nó làm đối số, chúng ta tạo hai bộ dao động thay vì chỉ một, chúng ta bắt đầu âm thanh ngay lập tức thay vì chấp nhận thời gian và chúng ta tắt âm thanh ngay lập tức sau một nửa một giây chứ không phải chạy theo cấp số nhân.

Vì trọng tâm của chúng tôi là điều chỉnh hệ thống âm thanh thành Redux, tôi sẽ để bài viết css-trick mô tả các chi tiết của API Web Audio được chạm vào đây.

TouchTone.js

export default class TouchTone {

    constructor() {
        // Get the audio context
        this.context = new (window.AudioContext || window.webkitAudioContext)();
    }

    init() {

        // Create, amplify, and connect row oscillator
        this.rowOscillator = this.context.createOscillator();
        this.rowOscillator.type = 'sine';
        this.rowGain = this.context.createGain();
        this.rowOscillator.connect(this.rowGain);
        this.rowGain.connect(this.context.destination);

        // Create, amplify, and connect column oscillator
        this.colOscillator = this.context.createOscillator();
        this.colOscillator.type = 'sine';
        this.colGain= this.context.createGain();
        this.colOscillator.connect(this.colGain);
        this.colGain.connect(this.context.destination);

    }

    play(tones) {

        // initialize
        this.init();

        // Get the current time from the audio context
        const time = this.context.currentTime;

        // Load tones into oscillators
        this.rowOscillator.frequency.value = tones[0];
        this.colOscillator.frequency.value = tones[1];

        // Set gain and start oscillators
        this.rowGain.gain.setValueAtTime(1, time);
        this.colGain.gain.setValueAtTime(1, time);
        this.rowOscillator.start(time);
        this.colOscillator.start(time);

        // Set the stop time
        this.stop(time + .5);

    }

    stop(time) {

        // Ramp down gain and stop oscillators
        this.rowGain.gain.setValueAtTime(0, time);
        this.colGain.gain.setValueAtTime(0, time);
        this.rowOscillator.stop(time);
        this.colOscillator.stop(time);
    }

}

Phần mềm trung gian Redux

Khi bạn lần đầu tiên đọc phần  giới thiệu chính thức  về Redux middleware, nó có thể dễ dàng vặn dưa của bạn. Điều này là do họ đã trải qua cả một loạt những cách sai lầm của người Bỉ để làm mọi việc trước khi đi đến giải pháp của họ. Đó là lý do tại sao tôi nghĩ sẽ rất tốt nếu có một ví dụ đơn giản đã chết để giúp bạn bắt đầu.

Nó thực sự không phức tạp chút nào. Một hàm đơn giản chấp nhận lưu trữ Redux và trả về một hàm chấp nhận phần trung gian tiếp theo trong cuộc gọi lại của chuỗi. Cuộc gọi lại đó chấp nhận một hành động. Bên trong chức năng đó, chúng ta có thể xử lý hoặc bỏ qua bất kỳ hành động nào đi qua, nhưng, khi chúng ta hoàn thành, chúng ta cần gọi phần gọi lại tiếp theo của phần mềm trung gian, chuyển nó thành hành động.

Điều quan trọng cần nhớ là nó sẽ xuất hiện trong vòng đời của ứng dụng và nó sẽ có cơ hội xử lý mọi hành động được gửi đi. Điều này làm cho nó trở thành một trung gian hoàn hảo để tương tác với các phần không tuần tự hóa trong các ứng dụng của chúng tôi như ổ cắm hoặc các thành phần âm thanh. Nó sẽ có quyền truy cập vào cửa hàng, vì vậy bạn có thể tham khảo trạng thái và bạn có thể gửi các hành động từ nó nếu bạn cần. Trong trường hợp của chúng tôi, chúng tôi sẽ chỉ phản hồi một hành động được gửi bởi bàn phím.

middleware.js

import TouchTone from './TouchTone';
import {PLAY_DTMF_PAIR} from "./actions";

export const audioMiddleware = store => {

    const touchTone = new TouchTone();

    return next => action => {

        switch (action.type) {

            case PLAY_DTMF_PAIR:
                touchTone.play(action.tones);
                break;

            default:
                break;

        }

        next(action);
    }

};

Phần kết luận

Rõ ràng, với một giao diện và hệ thống âm thanh khác, ứng dụng này có thể thực hiện một thiết bị âm nhạc, kích hoạt nhiều âm thanh dễ chịu hơn nhiều so với DTMF. Trọng tâm chính là làm thế nào để thích ứng với một hệ thống như vậy.

Sử dụng phần mềm trung gian để kiểm soát API Web âm thanh khá dễ dàng và về mặt kiến ​​trúc là cách đúng đắn để đi trong ứng dụng React / Redux. Vì vậy, đừng để tài liệu phức tạp trên trang Redux khiến bạn thất vọng nếu bạn chưa bao giờ xây dựng một phần mềm trung gian.

Những điều tham vọng hơn có thể xảy ra trong phần mềm trung gian là nó có thể phản hồi các sự kiện từ  AudioListener  hoặc  ScriptProcessorNode  và gửi các hành động bằng cách gọi  store.dispatch(). Điều này sẽ cho phép ứng dụng thực hiện không gian âm thanh hoặc trực quan hóa.

Bạn có thể tải xuống dự án từ GitHub:  https://github.com/cliffhall/react-dtmf-dialer

Hữu ích 1 Chia sẻ Viết bình luận 0
Đã xem 3882