import { TNativeAudioDestinationNode, TNativeAudioDestinationNodeFactoryFactory, TNativeGainNode } from '../types'; export const createNativeAudioDestinationNodeFactory: TNativeAudioDestinationNodeFactoryFactory = ( createNativeGainNode, overwriteAccessors ) => { return (nativeContext, channelCount, isNodeOfNativeOfflineAudioContext) => { const nativeAudioDestinationNode = nativeContext.destination; // Bug #132: Safari does not have the correct channelCount. if (nativeAudioDestinationNode.channelCount !== channelCount) { try { nativeAudioDestinationNode.channelCount = channelCount; } catch { // Bug #169: Safari throws an error on each attempt to change the channelCount. } } // Bug #83: Safari does not have the correct channelCountMode. if (isNodeOfNativeOfflineAudioContext && nativeAudioDestinationNode.channelCountMode !== 'explicit') { nativeAudioDestinationNode.channelCountMode = 'explicit'; } // Bug #47: The AudioDestinationNode in Safari does not initialize the maxChannelCount property correctly. if (nativeAudioDestinationNode.maxChannelCount === 0) { Object.defineProperty(nativeAudioDestinationNode, 'maxChannelCount', { value: channelCount }); } // Bug #168: No browser does yet have an AudioDestinationNode with an output. const gainNode = createNativeGainNode(nativeContext, { channelCount, channelCountMode: nativeAudioDestinationNode.channelCountMode, channelInterpretation: nativeAudioDestinationNode.channelInterpretation, gain: 1 }); overwriteAccessors( gainNode, 'channelCount', (get) => () => get.call(gainNode), (set) => (value) => { set.call(gainNode, value); try { nativeAudioDestinationNode.channelCount = value; } catch (err) { // Bug #169: Safari throws an error on each attempt to change the channelCount. if (value > nativeAudioDestinationNode.maxChannelCount) { throw err; } } } ); overwriteAccessors( gainNode, 'channelCountMode', (get) => () => get.call(gainNode), (set) => (value) => { set.call(gainNode, value); nativeAudioDestinationNode.channelCountMode = value; } ); overwriteAccessors( gainNode, 'channelInterpretation', (get) => () => get.call(gainNode), (set) => (value) => { set.call(gainNode, value); nativeAudioDestinationNode.channelInterpretation = value; } ); Object.defineProperty(gainNode, 'maxChannelCount', { get: () => nativeAudioDestinationNode.maxChannelCount }); // @todo This should be disconnected when the context is closed. gainNode.connect(nativeAudioDestinationNode); return <{ maxChannelCount: TNativeAudioDestinationNode['maxChannelCount'] } & TNativeGainNode>gainNode; }; };