Using multiple instances of a library that returns an instance

117 Views Asked by At

I am using a library (Voice) that returns an instance of it. I made following component that uses it successfully

But I need multiple instances of following component in an other component. Where each component will have a unique Voice instance

So I needed to clone the imported Voice object to create separate/new instance for each instance of following component.

This component receives a property *unique_key* for its identification at any point. I also bind that key to (cloned) SpeechInstance for its identification

import React, { Component } from 'react';
import { Text, View, Button} from 'react-native';
import Voice from '@react-native-voice/voice';

var all_speech_instances = [];

export default class VoiceRecorder extends Component {
    constructor(props) {
        super(props);            

        //like all_instances array Voice is global and its an instance returned by libraray
        // So i am cloning it to make specific/new for each instance of this component
        this.SpeechInstance = Object.assign(Object.create(Object.getPrototypeOf(Voice)), Voice);
        this.SpeechInstance.unique_key = props.unique_key;

        let obj_this = this;
        this.SpeechInstance.onSpeechError = function(e) { obj_this.onSpeechError(e) };
        this.SpeechInstance.onSpeechResults = function(e) { obj_this.onSpeechResults(e) };

        //Shown for proof of concept        
        all_speech_instances.push(this.SpeechInstance);
        if(all_speech_instances.length > 1){
            console.log('Instance 1 => unique_key='+all_speech_instances[0].unique_key); //shows 1
            console.log('Instance 2 => unique_key='+all_speech_instances[1].unique_key); //shows 2
        }
    }    

    _startRecognizing = async () => {
        await this.SpeechInstance.start();
    };
    _stopRecognizing = async () => {
        await this.SpeechInstance.stop();
    };

    onSpeechError(e) {
        // whenever error
        console.log('On speech error Key=' + this.SpeechInstance.unique_key);
        // always shows 2
    };
    onSpeechResults(e){        
        // whenever speech recognized
        console.log('On speech results Key=' + this.SpeechInstance.unique_key, e.value);
        //always shows 2
    };

    //Following tow functions are react-native specific
    componentWillUnmount() {
        this.SpeechInstance.removeAllListeners();
    }

    render() {
        let obj_this = this;
        return (
            <View style={{padding:10}}>
                <Text>unique_key: {this.SpeechInstance.unique_key}</Text>
                <View style={{padding: 5}}>
                    <Button onPress={()=>{ obj_this._startRecognizing()}} title='Start Listening' />
                </View>
                <View style={{padding: 5}}>
                    <Button onPress={()=>{ obj_this._stopRecognizing()}} title='Stop Listening' />
                </View>                                
            </View>
        );
    }
}

Usage I am using above component pretty straightforward

import React from 'react';
import {View} from 'react-native';
import SpeechComponent from '../voice/SpeechComponent';

export default class VoiceTestScreen extends React.Component {
    render(){
        return(
            <View>
                <SpeechComponent unique_key='1'/>
                <SpeechComponent unique_key='2'/>
            </View>
        );
    }
}

After very careful encapsulation to hide the SpeechInstance within the component instance so that it could not be shared gloabally, I still always result => console On speech results Key=2 weather I start recognition (started on button click) with first instance of component or second

1

There are 1 best solutions below

1
Sami On

This Explanation Needs Improvements: My question was not correct, @begi made that correction in comment, and also after reading the Api docs as he pointed, I got the idea that it was API specific problem, as single microphone can not be listened by multiple instances separately.

So I tried it an other way and got the solution.

import React from 'react';
import {View} from 'react-native';
import SpeechComponent from '../voice/SpeechComponent';

export default class TestMultiVoice extends React.Component {
    render(){
        return(
            <View>
                <SpeechComponent unique_key='1'/>
                <SpeechComponent unique_key='2'/>
            </View>
        );
    }
}

What I tricked was to clone the single static API instance Voice before Voice.start and then destroyed it completely onError and onResult.

import React, { Component } from 'react';
import { Text, View, Button} from 'react-native';
import Voice from '@react-native-voice/voice';

export default class SpeechComponent extends Component {
constructor(props) {
    super(props);
    this.state = {
        obtainedText: 'Recognized text will be displayed here'
    }
}    

_startRecognizing = async () => {
    let obj_this = this;
    this.SpeechInstance = Object.assign(Object.create(Object.getPrototypeOf(Voice)), Voice);
    this.SpeechInstance.onSpeechError = (e) => obj_this.onSpeechError(e);
    this.SpeechInstance.onSpeechResults = (e) => this.onSpeechResults(e);
    await this.SpeechInstance.start();
};
_stopRecognizing = async () => {
    await Voice.stop();
};

onSpeechError(e) {
    // whenever error
    console.log('On speech error Key=' + Voice.unique_key);
    this.onSpeechComplete();
    this.setState({obtainedText: e.error.message});
    // always shows 2
};
onSpeechResults(e){        
    // whenever speech recognized
    console.log('On speech results Key=' + Voice.unique_key, e.value);
    this.onSpeechComplete();
    this.setState({obtainedText: e.value[0]})
    //always shows 2
};

onSpeechComplete(){            
    this.SpeechInstance.destroy().then(this.SpeechInstance.removeAllListeners);
}

render() {
    let obj_this = this;
    return (
        <View style={{padding:10}}>
            <Text>unique_key: {Voice.unique_key}</Text>
            <View style={{padding: 5}}>
                <Button onPress={()=>{ obj_this._startRecognizing()}} title='Start Listening' />
            </View>
            <View style={{padding: 5}}>
                <Button onPress={()=>{ obj_this._stopRecognizing()}} title='Stop Listening' />
            </View>
            <View>
                <Text>Obtained Tex: {this.state.obtainedText}</Text>
            </View>
        </View>
    );
}

}