Skip to content

Commit 3ac8d44

Browse files
committed
feat: add audio waveform generation for WhatsApp voice messages
- Import audio-decode library for PCM audio analysis - Add getAudioDuration() helper to extract audio duration in seconds - Add getAudioWaveform() helper to generate 64-value waveform array - Modify audioWhatsapp() to include seconds and waveform in message metadata - Support waveform generation for both encoded and non-encoded audio paths - Fallback to default values (1 second, flat waveform) if generation fails This enables proper waveform visualization in WhatsApp voice messages, addressing issue #1587 in Baileys library.
1 parent 3454bec commit 3ac8d44

File tree

1 file changed

+54
-2
lines changed

1 file changed

+54
-2
lines changed

src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import useMultiFileAuthStatePrisma from '@utils/use-multi-file-auth-state-prisma
9090
import { AuthStateProvider } from '@utils/use-multi-file-auth-state-provider-files';
9191
import { useMultiFileAuthStateRedisDb } from '@utils/use-multi-file-auth-state-redis-db';
9292
import axios from 'axios';
93+
import audioDecode from 'audio-decode';
9394
import makeWASocket, {
9495
AnyMessageContent,
9596
BufferedEventData,
@@ -3038,6 +3039,42 @@ export class BaileysStartupService extends ChannelStartupService {
30383039
}
30393040
}
30403041

3042+
private async getAudioDuration(audioBuffer: Buffer): Promise<number> {
3043+
try {
3044+
const audioData = await audioDecode(audioBuffer);
3045+
return Math.ceil(audioData.duration);
3046+
} catch (error) {
3047+
this.logger.warn('Failed to get audio duration, using default 1 second');
3048+
return 1;
3049+
}
3050+
}
3051+
3052+
private async getAudioWaveform(audioBuffer: Buffer): Promise<number[]> {
3053+
try {
3054+
const audioData = await audioDecode(audioBuffer);
3055+
const samples = audioData.channelData[0]; // Get first channel
3056+
const waveformLength = 64;
3057+
const samplesPerWaveform = Math.floor(samples.length / waveformLength);
3058+
3059+
const waveform: number[] = [];
3060+
for (let i = 0; i < waveformLength; i++) {
3061+
const start = i * samplesPerWaveform;
3062+
const end = start + samplesPerWaveform;
3063+
let sum = 0;
3064+
for (let j = start; j < end && j < samples.length; j++) {
3065+
sum += Math.abs(samples[j]);
3066+
}
3067+
const avg = sum / samplesPerWaveform;
3068+
waveform.push(Math.floor(avg * 100));
3069+
}
3070+
3071+
return waveform;
3072+
} catch (error) {
3073+
this.logger.warn('Failed to generate waveform, using default');
3074+
return new Array(64).fill(50);
3075+
}
3076+
}
3077+
30413078
public async audioWhatsapp(data: SendAudioDto, file?: any, isIntegration = false) {
30423079
const mediaData: SendAudioDto = { ...data };
30433080

@@ -3056,9 +3093,12 @@ export class BaileysStartupService extends ChannelStartupService {
30563093
const convert = await this.processAudio(mediaData.audio);
30573094

30583095
if (Buffer.isBuffer(convert)) {
3096+
const seconds = await this.getAudioDuration(convert);
3097+
const waveform = await this.getAudioWaveform(convert);
3098+
30593099
const result = this.sendMessageWithTyping<AnyMessageContent>(
30603100
data.number,
3061-
{ audio: convert, ptt: true, mimetype: 'audio/ogg; codecs=opus' },
3101+
{ audio: convert, ptt: true, mimetype: 'audio/ogg; codecs=opus', seconds, waveform },
30623102
{ presence: 'recording', delay: data?.delay },
30633103
isIntegration,
30643104
);
@@ -3069,12 +3109,24 @@ export class BaileysStartupService extends ChannelStartupService {
30693109
}
30703110
}
30713111

3112+
const audioBuffer = isURL(data.audio) ? { url: data.audio } : Buffer.from(data.audio, 'base64');
3113+
let seconds: number | undefined;
3114+
let waveform: number[] | undefined;
3115+
3116+
// Only generate waveform for buffers, not URLs
3117+
if (Buffer.isBuffer(audioBuffer)) {
3118+
seconds = await this.getAudioDuration(audioBuffer);
3119+
waveform = await this.getAudioWaveform(audioBuffer);
3120+
}
3121+
30723122
return await this.sendMessageWithTyping<AnyMessageContent>(
30733123
data.number,
30743124
{
3075-
audio: isURL(data.audio) ? { url: data.audio } : Buffer.from(data.audio, 'base64'),
3125+
audio: audioBuffer,
30763126
ptt: true,
30773127
mimetype: 'audio/ogg; codecs=opus',
3128+
...(seconds !== undefined && { seconds }),
3129+
...(waveform !== undefined && { waveform }),
30783130
},
30793131
{ presence: 'recording', delay: data?.delay },
30803132
isIntegration,

0 commit comments

Comments
 (0)