{"version":3,"sources":["jsdelivr-header.js","/npm/wx-voice@0.2.1/index.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA,ACEA,aAEA,MAAM,aAAe,QAAQ,WACvB,MAAE,OAAU,QAAQ,iBACpB,OAAS,QAAQ,iBAEjB,GAAK,QAAQ,MACb,GAAK,QAAQ,MACb,KAAO,QAAQ,QACf,MAAQ,QAAQ,SAChB,QAAU,QAAQ,mBAClB,UAAY,QAAQ,cACpB,WAAa,QAAQ,cAI3B,MAAM,gBAAgB,aAElB,WAAA,CAAY,EAAU,GAAG,SAAU,GAC/B,QAEA,KAAK,SAAc,KAAK,QAAQ,GAChC,KAAK,YAAc,EAGnB,KAAK,oBACT,CAGA,MAAA,CAAO,EAAO,EAAQ,EAAS,GAC3B,IAAI,EACA,EAGJ,EAAS,KAAK,QAAQ,GACtB,EAAS,KAAK,QAAQ,QAGN,IAAZ,IACA,EAAU,CAAC,QAIQ,IAAnB,EAAQ,SAGM,MAFd,EAAM,KAAK,QAAQ,IAEX,KACJ,EAAM,EAAI,OAAO,IAErB,EAAQ,OAAS,GAIrB,EAAW,iBAAiB,GAG5B,IAEI,EAAa,SADA,UAAU,KAAK,EAAO,EAAG,MAE1C,CAAE,MAAO,GAEL,OADA,KAAK,KAAK,QAAS,GACZ,GACX,CAIA,GAAI,GAAiC,cAAnB,EAAW,KAAsB,CAG/C,IAAI,EAAmC,OAAlB,EAAQ,OACzB,EAAiB,GAAa,EAAQ,UAAa,EAAQ,UAAY,KAG3E,KAAK,YAAY,EAAO,GAAgB,IACpC,EAAQ,GAAY,EAGhB,GAAa,EACb,KAAK,EAAU,GAAS,IACpB,KAAK,gBAAgB,GACrB,EAAS,OAAM,EAAY,EAAO,IAKtC,KAAK,SAAqB,MAAZ,GAAuB,EAAO,EAAO,EAAQ,GAAU,IACjE,KAAK,gBAAgB,GACrB,EAAS,EAAI,GAErB,GAGR,MAGI,KAAK,SAAS,GAAQ,IAClB,EAAQ,GAAY,EACpB,KAAK,UAAS,GAAO,EAAO,EAAO,EAAQ,GAAU,IACjD,KAAK,gBAAgB,GACrB,EAAS,EAAI,GACf,GAId,CAGA,MAAA,CAAO,EAAO,EAAQ,EAAS,GAC3B,IAAI,EAAK,EAyBT,GAtBA,EAAS,KAAK,QAAQ,GACtB,EAAS,KAAK,QAAQ,QAGN,IAAZ,IACA,EAAU,CAAC,QAIQ,IAAnB,EAAQ,SAGM,MAFd,EAAM,KAAK,QAAQ,IAEX,KACJ,EAAM,EAAI,OAAO,IAErB,EAAQ,OAAS,GAIrB,EAAW,iBAAiB,IAGvB,GAAG,WAAW,GAEf,OADA,KAAK,KAAK,QAAS,mDAAqD,EAAQ,KACzE,IAIX,GAAsB,QAAlB,EAAQ,QAAsC,YAAlB,EAAQ,OAEpC,EAAW,KAAK,aAAa,EAAQ,QACrC,KAAK,UAAS,GAAO,EAAM,EAAO,EAAU,GAAU,IAC9C,EACA,KAAK,YAAY,EAAY,EAAQ,EAAQ,QAAS,IAClD,KAAK,gBAAgB,GACrB,EAAS,EAAI,IAGjB,GACJ,QAGD,IAAsB,QAAlB,EAAQ,OAiBf,OADA,KAAK,KAAK,QAAS,IAAI,MAAM,EAAQ,OAAS,wEACvC,IAfP,EAAW,KAAK,aAAa,EAAQ,cAErC,KAAK,UAAS,GAAO,EAAM,EAAO,EAAU,GAAU,IAC9C,EACA,KAAK,YAAY,EAAY,GAAS,IAClC,KAAK,gBAAgB,GACrB,EAAS,EAAI,IAGjB,GACJ,GAMR,CACJ,CAGA,QAAA,CAAS,EAAU,GACf,OAAO,QAAQ,GAAU,CAAC,EAAK,KAC3B,GAAI,EAAK,OAAO,EAAS,GAEzB,GAAI,GAAY,EAAS,OAAQ,CAC7B,IAAI,EAAW,WAAW,EAAS,OAAO,UAC1C,EAAY,MAAM,GAAa,EAAI,CACvC,CAEA,EAAS,EAAS,GAE1B,CAGA,QAAA,CAAS,EAAU,EAAW,EAAO,EAAQ,EAAS,GAElD,IAAI,GAAY,EACZ,EAAY,EAAQ,OACpB,EAAY,EAAQ,QACpB,EAAY,EAAQ,UACpB,EAAY,EAAQ,SACpB,EAAS,OAAO,GACX,GAAG,SAiCZ,SAAiB,GACb,GAAU,CACd,IAlCS,GAAG,SAoCZ,SAAiB,EAAK,EAAQ,GAC1B,GAAU,EACV,GACJ,IAtCS,GAAG,OAwCZ,SAAe,EAAQ,GACf,IACA,GAAU,EACV,EAAS,GAEjB,IA1CI,EACA,EAAS,EAAO,YAAY,SAAS,aAAa,CAAC,YAAa,UACzD,IACO,QAAV,GAA8B,YAAV,GACpB,EAAS,QACT,EAAS,EAAO,cAAc,CAAC,YAAa,WAC3B,QAAV,IACP,EAAS,EAAO,cAAc,CAAC,YAAa,UAAU,WAAW,UAKrE,IAAa,EAAS,EAAO,aAAa,IAC1C,IAAa,EAAS,EAAO,eAAe,IAC5C,IAAa,EAAS,EAAO,cAAc,KAI3C,EADU,OAAV,EACS,EAAO,WAAW,OACV,OAAV,EACE,EAAO,OAAO,SAEd,EAAO,OAAO,IAIpB,UAAU,KAAK,EAiB1B,CAGA,WAAA,CAAY,EAAO,EAAW,GAC1B,IAAI,EAAU,KAAK,aAAa,EAAQ,QACpC,EAAU,MAAM,KAAK,YAAY,WAAY,CAAC,EAAO,EAAQ,UAAW,IAG5E,EAAQ,OAAO,GAAG,QAAS,IAAD,IAC1B,EAAQ,OAAO,GAAG,QAAS,IAAD,IAE1B,EAAQ,GAAG,SAAU,IACL,GAAR,EACA,IAEA,EAAS,EACb,GAER,CAGA,WAAA,CAAY,EAAO,EAAQ,EAAM,GAC7B,IAAI,EAAmB,YAAR,EAAqB,eAAiB,WACjD,EAAU,MAAM,KAAK,YAAY,WAAY,CAAC,EAAO,EAAQ,IAGjE,EAAQ,OAAO,GAAG,QAAS,IAAD,IAC1B,EAAQ,OAAO,GAAG,QAAS,IAAD,IAE1B,EAAQ,GAAG,SAAU,IACL,GAAR,EACA,IAEA,EAAS,EACb,GAER,CAGA,QAAA,CAAS,EAAO,GACZ,IAAI,EAAS,KAAK,aAAa,EAAQ,SACnC,EAAS,GAEb,GAAG,SAAS,GAAO,CAAC,EAAK,KACrB,GAAI,EAAK,OAAO,KAGhB,EAAS,EAAK,YACH,WAAW,2BAClB,KAAK,WAAW,EAAQ,EAAQ,GAEhC,GACJ,GAER,CAGA,UAAA,CAAW,EAAQ,EAAQ,GACvB,IAAI,EAGJ,IACI,EAAS,QAAQ,OAAO,EAC5B,CAAE,MAAO,GACL,OAAO,GACX,CAGA,GAAG,UAAU,EAAQ,GAAS,IAC1B,GAAI,EAAK,OAAO,IAChB,EAAS,EAAO,GAExB,CAGA,WAAA,CAAY,EAAO,EAAQ,GACvB,IAAI,EAAM,GAEV,GAAG,SAAS,GAAO,CAAC,EAAK,KACrB,GAAI,EAAK,OAAO,IAChB,EAAM,QAAQ,OAAO,EAAM,cAG3B,GAAG,UAAU,EAAQ,GAAM,IACvB,GAAI,EAAK,OAAO,IAChB,EAAS,EAAO,GAClB,GAEV,CAGA,kBAAA,GACI,IAAI,EAAe,KAAK,YAAY,WAChC,EAAe,KAAK,YAAY,WAChC,EAAe,KAAK,YAGxB,IAAK,GAAG,WAAW,KAAiB,GAAG,WAAW,GAC9C,MAAM,IAAI,MAAM,8EAGpB,GAAI,GAAc,GAAG,WAAW,GAC5B,KAAK,YAAc,KAAK,QAAQ,GAChC,OAAO,cAAc,KAAK,iBAEvB,MAAI,EAAa,KAAK,kBAIzB,MAAM,IAAI,MAAM,oBAHhB,KAAK,YAAc,KAAK,QAAQ,EAIpC,CACJ,CAGA,WAAA,CAAY,GACR,OAAO,KAAK,QAAQ,UAAW,OAAQ,EAC3C,CAGA,cAAA,GAEI,IAAI,EAYJ,OADa,QAPT,EADA,QAAQ,IAAI,aAAe,GAAG,WAAW,QAAQ,IAAI,aAC9C,QAAQ,IAAI,YAGZ,MAAM,KAAK,SAAU,CAAE,SAAS,MAIb,KAAT,IAAa,OAAO,GAClC,CACX,CAGA,YAAA,CAAa,EAAU,GACnB,IAAI,EAAO,KAAK,SAAS,GAKzB,OAHK,IACD,EAAO,WAAW,KAAM,IAAM,IAAM,GAEjC,KAAK,QAAQ,KAAK,SAAU,EACvC,CAGA,eAAA,CAAgB,GACR,IACA,EAAW,KAAK,aAAa,GAAU,GACvC,GAAG,OAAO,GAAU,SAE5B,EAOJ,SAAS,SAAS,GACd,MAAM,EAAM,IAAI,WAAW,GAE3B,KAAM,GAAO,EAAI,OAAS,GACtB,OAAO,KAGX,MAAM,EAAS,IACX,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IAC/B,GAAI,EAAO,KAAO,EAAI,GAClB,OAAO,EAIf,OAAO,CAAI,EAGf,OAAI,EAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,MAC3C,EAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,MACvD,EAAM,CAAC,EAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,MAC7D,EAAM,CAAC,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,EAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,GAAM,KAE1F,CACH,IAAK,MACL,KAAM,cAIP,IACX,CAEA,SAAS,WAAW,GAChB,OAAQ,GAAkB,mBAAN,CACxB,CAEA,SAAS,iBAAiB,GACtB,OAAQ,WAAW,GAAK,EAAI,WAAa,CAC7C,CAEA,SAAS,KAAK,EAAQ,EAAQ,GAC1B,IAAI,GAAY,EAEZ,EAAK,GAAG,iBAAiB,GACzB,EAAK,GAAG,kBAAkB,GAO9B,SAAS,EAAK,GACL,IACD,GAAY,EACZ,EAAS,GAEjB,CAVA,EAAG,GAAG,QAAS,GACf,EAAG,GAAG,QAAS,GACf,EAAG,GAAG,SAAU,IAAS,GAAM,IAC/B,EAAG,KAAK,EAQZ,CAIA,OAAO,QAAU","file":"/npm/wx-voice@0.2.1/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/wx-voice@0.2.1/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/*\n    [wx-voice]\n    Convert audio files between Tencent apps (Weixin / Wechat, QQ) and Silk codec with other general format such as MP3 and M4A\n    \n    Github: https://github.com/Ang-YC/wx-voice\n    Author: AngYC <me@angyc.com>\n*/\n\n'use strict';\n\nconst EventEmitter = require('events');\nconst { spawn } = require('child_process');\nconst Ffmpeg = require('fluent-ffmpeg');\n\nconst os = require('os');\nconst fs = require('fs');\nconst path = require('path');\nconst which = require('which');\nconst dataUri = require('strong-data-uri');\nconst readChunk = require('read-chunk');\nconst randomatic = require('randomatic');\n\n\n\nclass WxVoice extends EventEmitter {\n\n    constructor(tempDir = os.tmpdir(), ffmpegPath) {\n        super();\n\n        this._tempDir    = path.resolve(tempDir);\n        this._ffmpegPath = ffmpegPath;\n\n        // Check if dependencies are available\n        this._checkDependencies();\n    }\n\n\n    decode(input, output, options, callback) {\n        var ext, buffer,\n            fileFormat;\n\n        // Make it into absolute path\n        input  = path.resolve(input);\n        output = path.resolve(output);\n\n        // Set options default to {}\n        if (options === undefined) {\n            options = {};\n        }\n\n        // Set format as extension if undefined\n        if (options.format === undefined) {\n            ext = path.extname(output);\n\n            if (ext[0] == \".\")\n                ext = ext.substr(1);\n\n            options.format = ext;\n        }\n\n        // Callback after decode is done\n        callback = validateFunction(callback);\n\n        // Check if file exists and get file format\n        try {\n            buffer     = readChunk.sync(input, 0, 4100);\n            fileFormat = fileType(buffer);\n        } catch (e) {\n            this.emit(\"error\", e);\n            return callback();\n        }\n\n\n        // Check if file is silk, webm or others\n        if (fileFormat && fileFormat.mime == \"audio/silk\") {\n\n            // Default frequency\n            var outputPCM     = (options.format == \"pcm\"),\n                silkFrequency = (outputPCM && options.frequency) ? options.frequency : 24000;\n\n            // Use Silk if it can be decoded\n            this._decodeSilk(input, silkFrequency, (tempFile) => {\n                input = tempFile || input;\n                \n                // Output raw PCM directly\n                if (outputPCM && tempFile) {\n                    copy(tempFile, output, (err) => {\n                        this._deleteTempFile(tempFile);\n                        callback(err ? undefined : output);\n                    });\n                \n                // Else Continue for other formats\n                } else {\n                    this._convert(tempFile != undefined, false, input, output, options, (res) => {\n                        this._deleteTempFile(tempFile);\n                        callback(res);\n                    });\n                }\n            });\n\n        } else {\n\n            // Use WebM output if it is WebM\n            this._tryWebM(input, (tempFile) => {\n                input = tempFile || input;\n                this._convert(false, false, input, output, options, (res) => {\n                    this._deleteTempFile(tempFile);\n                    callback(res);\n                });\n            });\n\n        }\n    }\n\n\n    encode(input, output, options, callback) {\n        var ext, tempFile;\n\n        // Make it into absolute path\n        input  = path.resolve(input);\n        output = path.resolve(output);\n\n        // Set options default to {}\n        if (options === undefined) {\n            options = {};\n        }\n\n        // Set format as extension if undefined\n        if (options.format === undefined) {\n            ext = path.extname(output);\n\n            if (ext[0] == \".\")\n                ext = ext.substr(1);\n\n            options.format = ext;\n        }\n\n        // Callback after encode is done\n        callback = validateFunction(callback);\n\n        // Check if file exists\n        if (!fs.existsSync(input)) {\n            this.emit(\"error\", \"Error: ENOENT: no such file or directory, open '\" + input + \"'\");\n            return callback();\n        }\n\n\n        if (options.format == \"silk\" || options.format == \"silk_amr\") {\n\n            tempFile = this._getTempFile(input + \".pcm\");\n            this._convert(false, true, input, tempFile, options, (tempOutput) => {\n                if (tempOutput) {\n                    this._encodeSilk(tempOutput, output, options.format, (res) => {\n                        this._deleteTempFile(tempOutput);\n                        callback(res);\n                    });\n                } else {\n                    callback();\n                }\n            });\n\n        } else if (options.format == \"webm\") {\n\n            tempFile = this._getTempFile(input + \".temp.webm\");\n\n            this._convert(false, true, input, tempFile, options, (tempOutput) => {\n                if (tempOutput) {\n                    this._encodeWebM(tempOutput, output, (res) => {\n                        this._deleteTempFile(tempOutput);\n                        callback(res);\n                    });\n                } else {\n                    callback();\n                }\n            });\n\n        } else {\n            this.emit(\"error\", new Error(options.format + \" is not a valid encode format, only silk, silk_amr and webm allowed\"));\n            return callback();\n        }\n    }\n\n\n    duration(filePath, callback) {\n        Ffmpeg.ffprobe(filePath, (err, metadata) => {\n            if (err) return callback(0);\n\n            if (metadata && metadata.format) {\n                var duration = parseFloat(metadata.format.duration);\n                duration = (isNaN(duration)) ? 0 : duration;\n            }\n\n            callback(duration);\n        });\n    }\n\n\n    _convert(rawInput, rawOutput, input, output, options, callback) {\n\n        var started   = false,\n            format    = options.format,\n            bitrate   = options.bitrate,\n            frequency = options.frequency,\n            channels  = options.channels,\n            ffmpeg = Ffmpeg(input)\n                .on(\"start\", onStart)\n                .on(\"error\", onError)\n                .on(\"end\", onEnd);\n\n        // Additional parameters for raw\n        if (rawInput) {\n            ffmpeg = ffmpeg.inputFormat(\"s16le\").inputOptions([\"-ar 24000\", \"-ac 1\"]);\n        } else if (rawOutput) {\n            if (format == \"silk\" || format == \"silk_amr\") {\n                format = \"s16le\";\n                ffmpeg = ffmpeg.outputOptions([\"-ar 24000\", \"-ac 1\"]);\n            } else if (format == \"webm\") {\n                ffmpeg = ffmpeg.outputOptions([\"-ar 48000\", \"-ac 1\"]).audioCodec(\"opus\");\n            }\n        }\n\n        // Other settings\n        if (bitrate)   { ffmpeg = ffmpeg.audioBitrate(bitrate); }\n        if (frequency) { ffmpeg = ffmpeg.audioFrequency(frequency); }\n        if (channels)  { ffmpeg = ffmpeg.audioChannels(channels); }\n\n        // Format dependent\n        if (format == \"m4a\") {\n            ffmpeg = ffmpeg.audioCodec(\"aac\");\n        } else if (format == \"pcm\") {\n            ffmpeg = ffmpeg.format(\"s16le\");\n        } else {\n            ffmpeg = ffmpeg.format(format);\n        }\n\n        // Output\n        ffmpeg.noVideo().save(output);\n\n        function onStart(commandLine) {\n            started = true;\n        }\n\n        function onError(err, stdout, stderr) {\n            started = false;\n            callback();\n        }\n\n        function onEnd(stdout, stderr) {\n            if (started) {\n                started = false;\n                callback(output);\n            }\n        }\n    }\n\n\n    _decodeSilk(input, frequency, callback) {\n        var output  = this._getTempFile(input + \".pcm\"),\n            decoder = spawn(this._getSilkSDK(\"decoder\"), [input, output, \"-Fs_API\", frequency]);\n\n        // Allow it to output\n        decoder.stdout.on('data', (data) => { });\n        decoder.stderr.on('data', (data) => { });\n\n        decoder.on('close', (code) => {\n            if (code == 1) { // Error occured\n                callback();\n            } else {         // Success\n                callback(output);\n            }\n        });\n    }\n\n\n    _encodeSilk(input, output, type, callback) {\n        var flag    = (type == \"silk_amr\" ? \"-tencent_amr\" : \"-tencent\"),\n            encoder = spawn(this._getSilkSDK(\"encoder\"), [input, output, flag]);\n\n        // Allow it to output\n        encoder.stdout.on('data', (data) => { });\n        encoder.stderr.on('data', (data) => { });\n\n        encoder.on('close', (code) => {\n            if (code == 1) { // Error occured\n                callback();\n            } else {         // Success\n                callback(output);\n            }\n        });\n    }\n\n\n    _tryWebM(input, callback) {\n        var output = this._getTempFile(input + \".webm\"),\n            base64 = \"\";\n\n        fs.readFile(input, (err, data) => {\n            if (err) return callback();\n\n            // Convert to string and check if Data URI is WebM\n            base64 = data.toString();\n            if (base64.startsWith(\"data:audio/webm;base64,\")) {\n                this._parseWebM(base64, output, callback);\n            } else {\n                callback();\n            }\n        });\n    }\n\n\n    _parseWebM(base64, output, callback) {\n        var buffer;\n\n        // Convert to buffer\n        try {\n            buffer = dataUri.decode(base64);\n        } catch (e) {\n            return callback();\n        }\n\n        // Write to file\n        fs.writeFile(output, buffer, (err) => {\n            if (err) return callback();\n            callback(output);\n        });\n    }\n\n\n    _encodeWebM(input, output, callback) {\n        var uri = \"\";\n\n        fs.readFile(input, (err, data) => {\n            if (err) return callback();\n            uri = dataUri.encode(data, \"audio/webm\");\n\n            // Write to file\n            fs.writeFile(output, uri, (err) => {\n                if (err) return callback();\n                callback(output);\n            }); \n        });\n    }\n\n\n    _checkDependencies() {\n        var silkDecoder  = this._getSilkSDK(\"decoder\"),\n            silkEncoder  = this._getSilkSDK(\"encoder\"),\n            ffmpegPath   = this._ffmpegPath;\n\n        // Check if Silk SDK is available\n        if (!fs.existsSync(silkDecoder) || !fs.existsSync(silkEncoder)) {\n            throw new Error(\"Silk SDK not found, make sure you compiled using command: wx-voice compile\");\n        }\n\n        if (ffmpegPath && fs.existsSync(ffmpegPath)) {\n            this._ffmpegPath = path.resolve(ffmpegPath);\n            Ffmpeg.setFfmpegPath(this._ffmpegPath);\n\n        } else if (ffmpegPath = this._getFfmpegPath()) {\n            this._ffmpegPath = path.resolve(ffmpegPath);\n\n        } else {\n            throw new Error(\"FFMPEG not found\");\n        }\n    }\n\n\n    _getSilkSDK(type) {\n        return path.resolve(__dirname, \"silk\", type);\n    }\n\n\n    _getFfmpegPath() {\n        // Get FFMPEG Path (Sync version of _getFfmpegPath in fluent-ffmpeg)\n        var path;\n\n        // Search in FFMPEG_PATH\n        if (process.env.FFMPEG_PATH && fs.existsSync(process.env.FFMPEG_PATH)) {\n            path = process.env.FFMPEG_PATH;\n        // Search in PATH, return null if not found\n        } else {\n            path = which.sync(\"ffmpeg\", { nothrow: true });\n        }\n\n        // Return undefined\n        if (path === null || path === \"\") path = undefined;\n        return path;\n    }\n\n\n    _getTempFile(fileName, noPrefix) {\n        var file = path.basename(fileName);\n\n        if (!noPrefix)\n            file = randomatic(\"a0\", 16) + \"_\" + file;\n\n        return path.resolve(this._tempDir, file);\n    }\n\n\n    _deleteTempFile(fileName) {\n        if (fileName) {\n            fileName = this._getTempFile(fileName, true);\n            fs.unlink(fileName, () => {});\n        }\n    }\n}\n\n\n\n// Utililities\n\nfunction fileType(input) {\n    const buf = new Uint8Array(input);\n\n    if (!(buf && buf.length > 1)) {\n        return null;\n    }\n\n    const check = (header) => {\n        for (let i = 0; i < header.length; i++) {\n            if (header[i] !== buf[i]) {\n                return false;\n            }\n        }\n\n        return true;\n    };\n\n    if (check([0x23, 0x21, 0x53, 0x49, 0x4C, 0x4B, 0x0A]) ||                   // Skype V1: #!SILK\\n  (https://tools.ietf.org/html/draft-spittka-silk-payload-format-00)\n        check([0x23, 0x21, 0x53, 0x49, 0x4C, 0x4B, 0x5F, 0x56, 0x33]) ||       // Skype V3: #!SILK_V3\n        check([0x02, 0x23, 0x21, 0x53, 0x49, 0x4C, 0x4B, 0x5F, 0x56, 0x33]) || // Tencent variation: .#!SILK_V3\n        check([0x23, 0x21, 0x41, 0x4D, 0x52, 0x0A, 0x02, 0x23, 0x21, 0x53, 0x49, 0x4C, 0x4B, 0x5F, 0x56, 0x33])) { // Tencent AMR variation: #!AMR\\n.#!SILK_V3\n\n        return {\n            ext: 'sil',\n            mime: 'audio/silk'\n        };\n    }\n\n    return null;\n}\n\nfunction isFunction(f) {\n    return (f && typeof f === 'function');\n}\n\nfunction validateFunction(f) {\n    return (isFunction(f) ? f : function() { });\n}\n\nfunction copy(source, target, callback) {\n    var completed = false;\n\n    var rd = fs.createReadStream(source);\n    var wr = fs.createWriteStream(target);\n\n    rd.on(\"error\", done);\n    wr.on(\"error\", done);\n    wr.on(\"close\", (ex) => { done(); });\n    rd.pipe(wr);\n\n    function done(err) {\n        if (!completed) {\n            completed = true;\n            callback(err);\n        }\n    }\n}\n\n\n\nmodule.exports = WxVoice;"]}