Skip to content

Commit

Permalink
feat: Added support to waveform for ptt audio messages
Browse files Browse the repository at this point in the history
  • Loading branch information
edgardmessias committed Mar 9, 2023
1 parent 229fb6f commit 290ebfe
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 1 deletion.
80 changes: 80 additions & 0 deletions src/chat/functions/prepareAudioWaveform.ts
@@ -0,0 +1,80 @@
/*!
* Copyright 2023 WPPConnect Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Debug from 'debug';

import type { AudioMessageOptions } from './sendFileMessage';

const debug = Debug('WA-JS:chat:sendFileMessage');

/**
* Prepare waveform form message audio file
*
* @category Message
* @internal
*/
export async function prepareAudioWaveform(
options: AudioMessageOptions,
file: File
): Promise<
| undefined
| {
duration: number;
waveform: Uint8Array;
}
> {
if (!options.isPtt || !options.waveform) {
return;
}

/**
* @see https://css-tricks.com/making-an-audio-waveform-visualizer-with-vanilla-javascript/
*/
try {
const audioData = await file.arrayBuffer();
const audioContext = new AudioContext();
const audioBuffer = await audioContext.decodeAudioData(audioData);

const rawData = audioBuffer.getChannelData(0); // We only need to work with one channel of data
const samples = 64; // Number of samples we want to have in our final data set
const blockSize = Math.floor(rawData.length / samples); // the number of samples in each subdivision
const filteredData = [];
for (let i = 0; i < samples; i++) {
const blockStart = blockSize * i; // the location of the first sample in the block
let sum = 0;
for (let j = 0; j < blockSize; j++) {
sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block
}
filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
}

// This guarantees that the largest data point will be set to 1, and the rest of the data will scale proportionally.
const multiplier = Math.pow(Math.max(...filteredData), -1);
const normalizedData = filteredData.map((n) => n * multiplier);

// Generate waveform like WhatsApp
const waveform = new Uint8Array(
normalizedData.map((n) => Math.floor(100 * n))
);

return {
duration: Math.floor(audioBuffer.duration),
waveform,
};
} catch (error) {
debug('Failed to generate waveform', error);
}
}
54 changes: 54 additions & 0 deletions src/chat/functions/sendFileMessage.ts
Expand Up @@ -39,6 +39,7 @@ import {
prepareMessageButtons,
prepareRawMessage,
} from '.';
import { prepareAudioWaveform } from './prepareAudioWaveform';

const debug = Debug('WA-JS:message');

Expand All @@ -54,9 +55,53 @@ export interface AutoDetectMessageOptions extends FileMessageOptions {
type: 'auto-detect';
}

/**
* Send an audio message as a PTT, like a recorded message
*
* @example
* ```javascript
* // PTT audio
* WPP.chat.sendFileMessage(
* '[number]@c.us',
* 'data:audio/mp3;base64,<a long base64 file...>',
* {
* type: 'audio',
* isPtt: true // false for common audio
* }
* );
* ```
*/
export interface AudioMessageOptions extends FileMessageOptions {
type: 'audio';
isPtt?: boolean;
/**
* Send an audio message as a PTT with waveform
*
* @example
* ```javascript
* // Enable waveform
* WPP.chat.sendFileMessage(
* '[number]@c.us',
* 'data:audio/mp3;base64,<a long base64 file...>',
* {
* type: 'audio',
* isPtt: true,
* waveform: true // false to disable
* }
* );
* // Disable waveform
* WPP.chat.sendFileMessage(
* '[number]@c.us',
* 'data:audio/mp3;base64,<a long base64 file...>',
* {
* type: 'audio',
* isPtt: true,
* waveform: false
* }
* );
* ```
*/
waveform?: boolean;
}

export interface DocumentMessageOptions
Expand Down Expand Up @@ -169,6 +214,7 @@ export async function sendFileMessage(
...defaultSendMessageOptions,
...{
type: 'auto-detect',
waveform: true,
},
...options,
};
Expand All @@ -189,12 +235,20 @@ export async function sendFileMessage(
asGif?: boolean;
isAudio?: boolean;
asSticker?: boolean;
precomputedFields?: {
duration: number;
waveform: Uint8Array;
};
} = {};

let isViewOnce: boolean | undefined;

if (options.type === 'audio') {
rawMediaOptions.isPtt = options.isPtt;
rawMediaOptions.precomputedFields = await prepareAudioWaveform(
options as any,
file
);
} else if (options.type === 'image') {
isViewOnce = options.isViewOnce;
} else if (options.type === 'video') {
Expand Down
3 changes: 2 additions & 1 deletion src/whatsapp/models/MsgModel.ts
Expand Up @@ -74,7 +74,8 @@ interface Props {
deprecatedMms3Url?: any;
directPath?: any;
mimetype?: any;
duration?: any;
waveform?: any;
duration?: number;
filehash?: any;
encFilehash?: any;
size?: any;
Expand Down

0 comments on commit 290ebfe

Please sign in to comment.