web-audio-recorder-ts 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,8 @@ export declare class Mp3LameEncoderWrapper implements AudioEncoder {
12
12
  private sampleRate;
13
13
  private numChannels;
14
14
  private bitrate;
15
+ private bufferCount;
16
+ private totalSamples;
15
17
  /**
16
18
  * Cria uma instância do encoder MP3 LAME
17
19
  *
@@ -12,6 +12,8 @@ export declare class OggVorbisEncoderWrapper implements AudioEncoder {
12
12
  private sampleRate;
13
13
  private numChannels;
14
14
  private quality;
15
+ private bufferCount;
16
+ private totalSamples;
15
17
  /**
16
18
  * Cria uma instância do encoder OGG Vorbis
17
19
  *
package/dist/index.cjs.js CHANGED
@@ -99,14 +99,36 @@ class WebAudioRecorder {
99
99
  }
100
100
  try {
101
101
  const inputBuffer = event.inputBuffer;
102
+ const actualChannels = inputBuffer.numberOfChannels;
102
103
  const buffers = [];
103
- // Extrair dados de cada canal
104
- for (let channel = 0; channel < this.numChannels; channel++) {
104
+ // Extrair dados de cada canal disponível
105
+ for (let channel = 0; channel < actualChannels; channel++) {
105
106
  const channelData = inputBuffer.getChannelData(channel);
106
107
  buffers.push(new Float32Array(channelData));
107
108
  }
109
+ // Se o número de canais do buffer não corresponde ao esperado, ajustar
110
+ if (actualChannels !== this.numChannels) {
111
+ if (actualChannels === 1 && this.numChannels === 2) {
112
+ // Mono -> Estéreo: duplicar o canal
113
+ buffers.push(new Float32Array(buffers[0]));
114
+ }
115
+ else if (actualChannels === 2 && this.numChannels === 1) {
116
+ // Estéreo -> Mono: usar apenas o primeiro canal
117
+ buffers.splice(1);
118
+ }
119
+ else {
120
+ // Outros casos: usar apenas os canais disponíveis até o limite esperado
121
+ while (buffers.length < this.numChannels && buffers.length > 0) {
122
+ // Duplicar o último canal se necessário
123
+ buffers.push(new Float32Array(buffers[buffers.length - 1]));
124
+ }
125
+ if (buffers.length > this.numChannels) {
126
+ buffers.splice(this.numChannels);
127
+ }
128
+ }
129
+ }
108
130
  // Codificar os dados
109
- if (this.encoder) {
131
+ if (this.encoder && buffers.length > 0) {
110
132
  this.encoder.encode(buffers);
111
133
  }
112
134
  }
@@ -147,6 +169,9 @@ class WebAudioRecorder {
147
169
  if (!this.encoder) {
148
170
  throw new Error('Encoder is not initialized');
149
171
  }
172
+ // Aguardar um pouco para garantir que todos os callbacks de áudio foram processados
173
+ // Isso evita condições de corrida onde finish() é chamado antes de todos os buffers serem processados
174
+ await new Promise(resolve => setTimeout(resolve, 50));
150
175
  const blob = this.encoder.finish(mimeType);
151
176
  const url = URL.createObjectURL(blob);
152
177
  const timecode = Date.now() - this.startTime;
@@ -643,15 +668,43 @@ class OggVorbisEncoderWrapper {
643
668
  */
644
669
  constructor(sampleRate, numChannels, options = {}) {
645
670
  this.encoder = null;
671
+ this.bufferCount = 0;
672
+ this.totalSamples = 0;
673
+ // Validar parâmetros
674
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0) {
675
+ throw new Error(`Invalid sampleRate: ${sampleRate}. Must be a positive number.`);
676
+ }
677
+ if (!Number.isInteger(numChannels) || numChannels < 1 || numChannels > 2) {
678
+ throw new Error(`Invalid numChannels: ${numChannels}. Must be 1 (mono) or 2 (stereo).`);
679
+ }
646
680
  this.sampleRate = sampleRate;
647
681
  this.numChannels = numChannels;
648
- this.quality = options.quality ?? 0.5;
682
+ // Validar e limitar qualidade (-0.1 a 1.0 para Vorbis)
683
+ const rawQuality = options.quality ?? 0.5;
684
+ if (!Number.isFinite(rawQuality)) {
685
+ console.warn(`Invalid quality value: ${rawQuality}. Using default 0.5`);
686
+ this.quality = 0.5;
687
+ }
688
+ else {
689
+ // Clamp quality to valid range
690
+ this.quality = Math.max(-0.1, Math.min(1.0, rawQuality));
691
+ if (rawQuality !== this.quality) {
692
+ console.warn(`Quality value ${rawQuality} clamped to valid range: ${this.quality}`);
693
+ }
694
+ }
649
695
  // Verificar se OggVorbisEncoder está disponível
650
696
  if (typeof OggVorbisEncoder === 'undefined') {
651
697
  throw new Error('OggVorbisEncoder is not loaded. Make sure to load OggVorbisEncoder.min.js before using this encoder.');
652
698
  }
653
- // Criar instância do encoder
654
- this.encoder = new OggVorbisEncoder(sampleRate, numChannels, this.quality);
699
+ try {
700
+ // Criar instância do encoder
701
+ this.encoder = new OggVorbisEncoder(sampleRate, numChannels, this.quality);
702
+ }
703
+ catch (error) {
704
+ const errorMsg = error instanceof Error ? error.message : String(error);
705
+ throw new Error(`Failed to initialize OGG encoder: ${errorMsg}. ` +
706
+ `Parameters: sampleRate=${sampleRate}, numChannels=${numChannels}, quality=${this.quality}`);
707
+ }
655
708
  }
656
709
  /**
657
710
  * Codifica buffers de áudio
@@ -665,7 +718,60 @@ class OggVorbisEncoderWrapper {
665
718
  if (buffers.length !== this.numChannels) {
666
719
  throw new Error(`Expected ${this.numChannels} channels, got ${buffers.length}`);
667
720
  }
668
- this.encoder.encode(buffers);
721
+ // Validar que todos os buffers têm o mesmo tamanho
722
+ if (buffers.length > 0) {
723
+ const expectedLength = buffers[0].length;
724
+ for (let i = 1; i < buffers.length; i++) {
725
+ if (buffers[i].length !== expectedLength) {
726
+ throw new Error(`Channel ${i} has length ${buffers[i].length}, expected ${expectedLength}`);
727
+ }
728
+ }
729
+ // Validar que há dados para processar
730
+ if (expectedLength === 0) {
731
+ // Buffer vazio, não há nada para codificar
732
+ return;
733
+ }
734
+ }
735
+ else {
736
+ // Nenhum buffer fornecido
737
+ return;
738
+ }
739
+ // Criar cópias dos buffers e validar valores (NaN, Infinity)
740
+ const safeBuffers = buffers.map((buffer, channelIndex) => {
741
+ const safeBuffer = new Float32Array(buffer.length);
742
+ let hasInvalidValues = false;
743
+ for (let i = 0; i < buffer.length; i++) {
744
+ const value = buffer[i];
745
+ // Verificar NaN e Infinity
746
+ if (!Number.isFinite(value)) {
747
+ hasInvalidValues = true;
748
+ // Substituir valores inválidos por 0
749
+ safeBuffer[i] = 0;
750
+ }
751
+ else {
752
+ // Clamp valores para o range válido de áudio (-1.0 a 1.0)
753
+ safeBuffer[i] = Math.max(-1, Math.min(1.0, value));
754
+ }
755
+ }
756
+ if (hasInvalidValues) {
757
+ console.warn(`OGG Encoder: Found invalid values (NaN/Infinity) in channel ${channelIndex}. ` +
758
+ `Replaced with 0. Buffer length: ${buffer.length}`);
759
+ }
760
+ return safeBuffer;
761
+ });
762
+ try {
763
+ this.encoder.encode(safeBuffers);
764
+ // Contar buffers processados para garantir que há dados antes de finalizar
765
+ this.bufferCount++;
766
+ this.totalSamples += safeBuffers[0].length;
767
+ }
768
+ catch (error) {
769
+ // Melhorar mensagem de erro para incluir informações de debug
770
+ const errorMsg = error instanceof Error ? error.message : String(error);
771
+ throw new Error(`OGG encoding error: ${errorMsg}. ` +
772
+ `Buffers: ${buffers.length} channels, lengths: ${buffers.map(b => b.length).join(', ')}, ` +
773
+ `Total buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}`);
774
+ }
669
775
  }
670
776
  /**
671
777
  * Finaliza o encoding e retorna o Blob OGG
@@ -677,7 +783,25 @@ class OggVorbisEncoderWrapper {
677
783
  if (!this.encoder) {
678
784
  throw new Error('Encoder is not initialized');
679
785
  }
680
- return this.encoder.finish(mimeType);
786
+ // Verificar se há dados processados
787
+ if (this.bufferCount === 0) {
788
+ console.warn('OGG Encoder: finish() called but no buffers were encoded. This may cause issues with the Emscripten encoder.');
789
+ // Ainda tentar finalizar, mas avisar
790
+ }
791
+ try {
792
+ const blob = this.encoder.finish(mimeType);
793
+ // Validar que o blob não está vazio
794
+ if (blob.size === 0) {
795
+ console.warn('OGG Encoder: finish() returned empty blob. This may indicate insufficient audio data was encoded.');
796
+ }
797
+ return blob;
798
+ }
799
+ catch (error) {
800
+ const errorMsg = error instanceof Error ? error.message : String(error);
801
+ throw new Error(`OGG finish() error: ${errorMsg}. ` +
802
+ `Buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}, ` +
803
+ `Sample rate: ${this.sampleRate}, Channels: ${this.numChannels}, Quality: ${this.quality}`);
804
+ }
681
805
  }
682
806
  /**
683
807
  * Cancela o encoding
@@ -687,6 +811,9 @@ class OggVorbisEncoderWrapper {
687
811
  this.encoder.cancel();
688
812
  this.encoder = null;
689
813
  }
814
+ // Reset contadores
815
+ this.bufferCount = 0;
816
+ this.totalSamples = 0;
690
817
  }
691
818
  }
692
819
  /**
@@ -841,15 +968,43 @@ class Mp3LameEncoderWrapper {
841
968
  */
842
969
  constructor(sampleRate, numChannels, options = {}) {
843
970
  this.encoder = null;
971
+ this.bufferCount = 0;
972
+ this.totalSamples = 0;
973
+ // Validar parâmetros
974
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0) {
975
+ throw new Error(`Invalid sampleRate: ${sampleRate}. Must be a positive number.`);
976
+ }
977
+ if (!Number.isInteger(numChannels) || numChannels < 1 || numChannels > 2) {
978
+ throw new Error(`Invalid numChannels: ${numChannels}. Must be 1 (mono) or 2 (stereo).`);
979
+ }
844
980
  this.sampleRate = sampleRate;
845
981
  this.numChannels = numChannels;
846
- this.bitrate = options.bitrate ?? 128;
982
+ // Validar e limitar bitrate (32 a 320 kbps para MP3)
983
+ const rawBitrate = options.bitrate ?? 128;
984
+ if (!Number.isFinite(rawBitrate) || !Number.isInteger(rawBitrate)) {
985
+ console.warn(`Invalid bitrate value: ${rawBitrate}. Using default 128`);
986
+ this.bitrate = 128;
987
+ }
988
+ else {
989
+ // Clamp bitrate to valid range
990
+ this.bitrate = Math.max(32, Math.min(320, rawBitrate));
991
+ if (rawBitrate !== this.bitrate) {
992
+ console.warn(`Bitrate value ${rawBitrate} clamped to valid range: ${this.bitrate}`);
993
+ }
994
+ }
847
995
  // Verificar se Mp3LameEncoder está disponível
848
996
  if (typeof Mp3LameEncoder === 'undefined') {
849
997
  throw new Error('Mp3LameEncoder is not loaded. Make sure to load Mp3LameEncoder.min.js before using this encoder.');
850
998
  }
851
- // Criar instância do encoder
852
- this.encoder = new Mp3LameEncoder(sampleRate, numChannels, this.bitrate);
999
+ try {
1000
+ // Criar instância do encoder
1001
+ this.encoder = new Mp3LameEncoder(sampleRate, numChannels, this.bitrate);
1002
+ }
1003
+ catch (error) {
1004
+ const errorMsg = error instanceof Error ? error.message : String(error);
1005
+ throw new Error(`Failed to initialize MP3 encoder: ${errorMsg}. ` +
1006
+ `Parameters: sampleRate=${sampleRate}, numChannels=${numChannels}, bitrate=${this.bitrate}`);
1007
+ }
853
1008
  }
854
1009
  /**
855
1010
  * Codifica buffers de áudio
@@ -863,7 +1018,60 @@ class Mp3LameEncoderWrapper {
863
1018
  if (buffers.length !== this.numChannels) {
864
1019
  throw new Error(`Expected ${this.numChannels} channels, got ${buffers.length}`);
865
1020
  }
866
- this.encoder.encode(buffers);
1021
+ // Validar que todos os buffers têm o mesmo tamanho
1022
+ if (buffers.length > 0) {
1023
+ const expectedLength = buffers[0].length;
1024
+ for (let i = 1; i < buffers.length; i++) {
1025
+ if (buffers[i].length !== expectedLength) {
1026
+ throw new Error(`Channel ${i} has length ${buffers[i].length}, expected ${expectedLength}`);
1027
+ }
1028
+ }
1029
+ // Validar que há dados para processar
1030
+ if (expectedLength === 0) {
1031
+ // Buffer vazio, não há nada para codificar
1032
+ return;
1033
+ }
1034
+ }
1035
+ else {
1036
+ // Nenhum buffer fornecido
1037
+ return;
1038
+ }
1039
+ // Criar cópias dos buffers e validar valores (NaN, Infinity)
1040
+ const safeBuffers = buffers.map((buffer, channelIndex) => {
1041
+ const safeBuffer = new Float32Array(buffer.length);
1042
+ let hasInvalidValues = false;
1043
+ for (let i = 0; i < buffer.length; i++) {
1044
+ const value = buffer[i];
1045
+ // Verificar NaN e Infinity
1046
+ if (!Number.isFinite(value)) {
1047
+ hasInvalidValues = true;
1048
+ // Substituir valores inválidos por 0
1049
+ safeBuffer[i] = 0;
1050
+ }
1051
+ else {
1052
+ // Clamp valores para o range válido de áudio (-1.0 a 1.0)
1053
+ safeBuffer[i] = Math.max(-1, Math.min(1.0, value));
1054
+ }
1055
+ }
1056
+ if (hasInvalidValues) {
1057
+ console.warn(`MP3 Encoder: Found invalid values (NaN/Infinity) in channel ${channelIndex}. ` +
1058
+ `Replaced with 0. Buffer length: ${buffer.length}`);
1059
+ }
1060
+ return safeBuffer;
1061
+ });
1062
+ try {
1063
+ this.encoder.encode(safeBuffers);
1064
+ // Contar buffers processados para garantir que há dados antes de finalizar
1065
+ this.bufferCount++;
1066
+ this.totalSamples += safeBuffers[0].length;
1067
+ }
1068
+ catch (error) {
1069
+ // Melhorar mensagem de erro para incluir informações de debug
1070
+ const errorMsg = error instanceof Error ? error.message : String(error);
1071
+ throw new Error(`MP3 encoding error: ${errorMsg}. ` +
1072
+ `Buffers: ${buffers.length} channels, lengths: ${buffers.map(b => b.length).join(', ')}, ` +
1073
+ `Total buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}`);
1074
+ }
867
1075
  }
868
1076
  /**
869
1077
  * Finaliza o encoding e retorna o Blob MP3
@@ -875,7 +1083,25 @@ class Mp3LameEncoderWrapper {
875
1083
  if (!this.encoder) {
876
1084
  throw new Error('Encoder is not initialized');
877
1085
  }
878
- return this.encoder.finish(mimeType);
1086
+ // Verificar se há dados processados
1087
+ if (this.bufferCount === 0) {
1088
+ console.warn('MP3 Encoder: finish() called but no buffers were encoded. This may cause issues with the Emscripten encoder.');
1089
+ // Ainda tentar finalizar, mas avisar
1090
+ }
1091
+ try {
1092
+ const blob = this.encoder.finish(mimeType);
1093
+ // Validar que o blob não está vazio
1094
+ if (blob.size === 0) {
1095
+ console.warn('MP3 Encoder: finish() returned empty blob. This may indicate insufficient audio data was encoded.');
1096
+ }
1097
+ return blob;
1098
+ }
1099
+ catch (error) {
1100
+ const errorMsg = error instanceof Error ? error.message : String(error);
1101
+ throw new Error(`MP3 finish() error: ${errorMsg}. ` +
1102
+ `Buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}, ` +
1103
+ `Sample rate: ${this.sampleRate}, Channels: ${this.numChannels}, Bitrate: ${this.bitrate}`);
1104
+ }
879
1105
  }
880
1106
  /**
881
1107
  * Cancela o encoding
@@ -885,6 +1111,9 @@ class Mp3LameEncoderWrapper {
885
1111
  this.encoder.cancel();
886
1112
  this.encoder = null;
887
1113
  }
1114
+ // Reset contadores
1115
+ this.bufferCount = 0;
1116
+ this.totalSamples = 0;
888
1117
  }
889
1118
  }
890
1119
  /**
package/dist/index.esm.js CHANGED
@@ -96,14 +96,36 @@ class WebAudioRecorder {
96
96
  }
97
97
  try {
98
98
  const inputBuffer = event.inputBuffer;
99
+ const actualChannels = inputBuffer.numberOfChannels;
99
100
  const buffers = [];
100
- // Extrair dados de cada canal
101
- for (let channel = 0; channel < this.numChannels; channel++) {
101
+ // Extrair dados de cada canal disponível
102
+ for (let channel = 0; channel < actualChannels; channel++) {
102
103
  const channelData = inputBuffer.getChannelData(channel);
103
104
  buffers.push(new Float32Array(channelData));
104
105
  }
106
+ // Se o número de canais do buffer não corresponde ao esperado, ajustar
107
+ if (actualChannels !== this.numChannels) {
108
+ if (actualChannels === 1 && this.numChannels === 2) {
109
+ // Mono -> Estéreo: duplicar o canal
110
+ buffers.push(new Float32Array(buffers[0]));
111
+ }
112
+ else if (actualChannels === 2 && this.numChannels === 1) {
113
+ // Estéreo -> Mono: usar apenas o primeiro canal
114
+ buffers.splice(1);
115
+ }
116
+ else {
117
+ // Outros casos: usar apenas os canais disponíveis até o limite esperado
118
+ while (buffers.length < this.numChannels && buffers.length > 0) {
119
+ // Duplicar o último canal se necessário
120
+ buffers.push(new Float32Array(buffers[buffers.length - 1]));
121
+ }
122
+ if (buffers.length > this.numChannels) {
123
+ buffers.splice(this.numChannels);
124
+ }
125
+ }
126
+ }
105
127
  // Codificar os dados
106
- if (this.encoder) {
128
+ if (this.encoder && buffers.length > 0) {
107
129
  this.encoder.encode(buffers);
108
130
  }
109
131
  }
@@ -144,6 +166,9 @@ class WebAudioRecorder {
144
166
  if (!this.encoder) {
145
167
  throw new Error('Encoder is not initialized');
146
168
  }
169
+ // Aguardar um pouco para garantir que todos os callbacks de áudio foram processados
170
+ // Isso evita condições de corrida onde finish() é chamado antes de todos os buffers serem processados
171
+ await new Promise(resolve => setTimeout(resolve, 50));
147
172
  const blob = this.encoder.finish(mimeType);
148
173
  const url = URL.createObjectURL(blob);
149
174
  const timecode = Date.now() - this.startTime;
@@ -640,15 +665,43 @@ class OggVorbisEncoderWrapper {
640
665
  */
641
666
  constructor(sampleRate, numChannels, options = {}) {
642
667
  this.encoder = null;
668
+ this.bufferCount = 0;
669
+ this.totalSamples = 0;
670
+ // Validar parâmetros
671
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0) {
672
+ throw new Error(`Invalid sampleRate: ${sampleRate}. Must be a positive number.`);
673
+ }
674
+ if (!Number.isInteger(numChannels) || numChannels < 1 || numChannels > 2) {
675
+ throw new Error(`Invalid numChannels: ${numChannels}. Must be 1 (mono) or 2 (stereo).`);
676
+ }
643
677
  this.sampleRate = sampleRate;
644
678
  this.numChannels = numChannels;
645
- this.quality = options.quality ?? 0.5;
679
+ // Validar e limitar qualidade (-0.1 a 1.0 para Vorbis)
680
+ const rawQuality = options.quality ?? 0.5;
681
+ if (!Number.isFinite(rawQuality)) {
682
+ console.warn(`Invalid quality value: ${rawQuality}. Using default 0.5`);
683
+ this.quality = 0.5;
684
+ }
685
+ else {
686
+ // Clamp quality to valid range
687
+ this.quality = Math.max(-0.1, Math.min(1.0, rawQuality));
688
+ if (rawQuality !== this.quality) {
689
+ console.warn(`Quality value ${rawQuality} clamped to valid range: ${this.quality}`);
690
+ }
691
+ }
646
692
  // Verificar se OggVorbisEncoder está disponível
647
693
  if (typeof OggVorbisEncoder === 'undefined') {
648
694
  throw new Error('OggVorbisEncoder is not loaded. Make sure to load OggVorbisEncoder.min.js before using this encoder.');
649
695
  }
650
- // Criar instância do encoder
651
- this.encoder = new OggVorbisEncoder(sampleRate, numChannels, this.quality);
696
+ try {
697
+ // Criar instância do encoder
698
+ this.encoder = new OggVorbisEncoder(sampleRate, numChannels, this.quality);
699
+ }
700
+ catch (error) {
701
+ const errorMsg = error instanceof Error ? error.message : String(error);
702
+ throw new Error(`Failed to initialize OGG encoder: ${errorMsg}. ` +
703
+ `Parameters: sampleRate=${sampleRate}, numChannels=${numChannels}, quality=${this.quality}`);
704
+ }
652
705
  }
653
706
  /**
654
707
  * Codifica buffers de áudio
@@ -662,7 +715,60 @@ class OggVorbisEncoderWrapper {
662
715
  if (buffers.length !== this.numChannels) {
663
716
  throw new Error(`Expected ${this.numChannels} channels, got ${buffers.length}`);
664
717
  }
665
- this.encoder.encode(buffers);
718
+ // Validar que todos os buffers têm o mesmo tamanho
719
+ if (buffers.length > 0) {
720
+ const expectedLength = buffers[0].length;
721
+ for (let i = 1; i < buffers.length; i++) {
722
+ if (buffers[i].length !== expectedLength) {
723
+ throw new Error(`Channel ${i} has length ${buffers[i].length}, expected ${expectedLength}`);
724
+ }
725
+ }
726
+ // Validar que há dados para processar
727
+ if (expectedLength === 0) {
728
+ // Buffer vazio, não há nada para codificar
729
+ return;
730
+ }
731
+ }
732
+ else {
733
+ // Nenhum buffer fornecido
734
+ return;
735
+ }
736
+ // Criar cópias dos buffers e validar valores (NaN, Infinity)
737
+ const safeBuffers = buffers.map((buffer, channelIndex) => {
738
+ const safeBuffer = new Float32Array(buffer.length);
739
+ let hasInvalidValues = false;
740
+ for (let i = 0; i < buffer.length; i++) {
741
+ const value = buffer[i];
742
+ // Verificar NaN e Infinity
743
+ if (!Number.isFinite(value)) {
744
+ hasInvalidValues = true;
745
+ // Substituir valores inválidos por 0
746
+ safeBuffer[i] = 0;
747
+ }
748
+ else {
749
+ // Clamp valores para o range válido de áudio (-1.0 a 1.0)
750
+ safeBuffer[i] = Math.max(-1, Math.min(1.0, value));
751
+ }
752
+ }
753
+ if (hasInvalidValues) {
754
+ console.warn(`OGG Encoder: Found invalid values (NaN/Infinity) in channel ${channelIndex}. ` +
755
+ `Replaced with 0. Buffer length: ${buffer.length}`);
756
+ }
757
+ return safeBuffer;
758
+ });
759
+ try {
760
+ this.encoder.encode(safeBuffers);
761
+ // Contar buffers processados para garantir que há dados antes de finalizar
762
+ this.bufferCount++;
763
+ this.totalSamples += safeBuffers[0].length;
764
+ }
765
+ catch (error) {
766
+ // Melhorar mensagem de erro para incluir informações de debug
767
+ const errorMsg = error instanceof Error ? error.message : String(error);
768
+ throw new Error(`OGG encoding error: ${errorMsg}. ` +
769
+ `Buffers: ${buffers.length} channels, lengths: ${buffers.map(b => b.length).join(', ')}, ` +
770
+ `Total buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}`);
771
+ }
666
772
  }
667
773
  /**
668
774
  * Finaliza o encoding e retorna o Blob OGG
@@ -674,7 +780,25 @@ class OggVorbisEncoderWrapper {
674
780
  if (!this.encoder) {
675
781
  throw new Error('Encoder is not initialized');
676
782
  }
677
- return this.encoder.finish(mimeType);
783
+ // Verificar se há dados processados
784
+ if (this.bufferCount === 0) {
785
+ console.warn('OGG Encoder: finish() called but no buffers were encoded. This may cause issues with the Emscripten encoder.');
786
+ // Ainda tentar finalizar, mas avisar
787
+ }
788
+ try {
789
+ const blob = this.encoder.finish(mimeType);
790
+ // Validar que o blob não está vazio
791
+ if (blob.size === 0) {
792
+ console.warn('OGG Encoder: finish() returned empty blob. This may indicate insufficient audio data was encoded.');
793
+ }
794
+ return blob;
795
+ }
796
+ catch (error) {
797
+ const errorMsg = error instanceof Error ? error.message : String(error);
798
+ throw new Error(`OGG finish() error: ${errorMsg}. ` +
799
+ `Buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}, ` +
800
+ `Sample rate: ${this.sampleRate}, Channels: ${this.numChannels}, Quality: ${this.quality}`);
801
+ }
678
802
  }
679
803
  /**
680
804
  * Cancela o encoding
@@ -684,6 +808,9 @@ class OggVorbisEncoderWrapper {
684
808
  this.encoder.cancel();
685
809
  this.encoder = null;
686
810
  }
811
+ // Reset contadores
812
+ this.bufferCount = 0;
813
+ this.totalSamples = 0;
687
814
  }
688
815
  }
689
816
  /**
@@ -838,15 +965,43 @@ class Mp3LameEncoderWrapper {
838
965
  */
839
966
  constructor(sampleRate, numChannels, options = {}) {
840
967
  this.encoder = null;
968
+ this.bufferCount = 0;
969
+ this.totalSamples = 0;
970
+ // Validar parâmetros
971
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0) {
972
+ throw new Error(`Invalid sampleRate: ${sampleRate}. Must be a positive number.`);
973
+ }
974
+ if (!Number.isInteger(numChannels) || numChannels < 1 || numChannels > 2) {
975
+ throw new Error(`Invalid numChannels: ${numChannels}. Must be 1 (mono) or 2 (stereo).`);
976
+ }
841
977
  this.sampleRate = sampleRate;
842
978
  this.numChannels = numChannels;
843
- this.bitrate = options.bitrate ?? 128;
979
+ // Validar e limitar bitrate (32 a 320 kbps para MP3)
980
+ const rawBitrate = options.bitrate ?? 128;
981
+ if (!Number.isFinite(rawBitrate) || !Number.isInteger(rawBitrate)) {
982
+ console.warn(`Invalid bitrate value: ${rawBitrate}. Using default 128`);
983
+ this.bitrate = 128;
984
+ }
985
+ else {
986
+ // Clamp bitrate to valid range
987
+ this.bitrate = Math.max(32, Math.min(320, rawBitrate));
988
+ if (rawBitrate !== this.bitrate) {
989
+ console.warn(`Bitrate value ${rawBitrate} clamped to valid range: ${this.bitrate}`);
990
+ }
991
+ }
844
992
  // Verificar se Mp3LameEncoder está disponível
845
993
  if (typeof Mp3LameEncoder === 'undefined') {
846
994
  throw new Error('Mp3LameEncoder is not loaded. Make sure to load Mp3LameEncoder.min.js before using this encoder.');
847
995
  }
848
- // Criar instância do encoder
849
- this.encoder = new Mp3LameEncoder(sampleRate, numChannels, this.bitrate);
996
+ try {
997
+ // Criar instância do encoder
998
+ this.encoder = new Mp3LameEncoder(sampleRate, numChannels, this.bitrate);
999
+ }
1000
+ catch (error) {
1001
+ const errorMsg = error instanceof Error ? error.message : String(error);
1002
+ throw new Error(`Failed to initialize MP3 encoder: ${errorMsg}. ` +
1003
+ `Parameters: sampleRate=${sampleRate}, numChannels=${numChannels}, bitrate=${this.bitrate}`);
1004
+ }
850
1005
  }
851
1006
  /**
852
1007
  * Codifica buffers de áudio
@@ -860,7 +1015,60 @@ class Mp3LameEncoderWrapper {
860
1015
  if (buffers.length !== this.numChannels) {
861
1016
  throw new Error(`Expected ${this.numChannels} channels, got ${buffers.length}`);
862
1017
  }
863
- this.encoder.encode(buffers);
1018
+ // Validar que todos os buffers têm o mesmo tamanho
1019
+ if (buffers.length > 0) {
1020
+ const expectedLength = buffers[0].length;
1021
+ for (let i = 1; i < buffers.length; i++) {
1022
+ if (buffers[i].length !== expectedLength) {
1023
+ throw new Error(`Channel ${i} has length ${buffers[i].length}, expected ${expectedLength}`);
1024
+ }
1025
+ }
1026
+ // Validar que há dados para processar
1027
+ if (expectedLength === 0) {
1028
+ // Buffer vazio, não há nada para codificar
1029
+ return;
1030
+ }
1031
+ }
1032
+ else {
1033
+ // Nenhum buffer fornecido
1034
+ return;
1035
+ }
1036
+ // Criar cópias dos buffers e validar valores (NaN, Infinity)
1037
+ const safeBuffers = buffers.map((buffer, channelIndex) => {
1038
+ const safeBuffer = new Float32Array(buffer.length);
1039
+ let hasInvalidValues = false;
1040
+ for (let i = 0; i < buffer.length; i++) {
1041
+ const value = buffer[i];
1042
+ // Verificar NaN e Infinity
1043
+ if (!Number.isFinite(value)) {
1044
+ hasInvalidValues = true;
1045
+ // Substituir valores inválidos por 0
1046
+ safeBuffer[i] = 0;
1047
+ }
1048
+ else {
1049
+ // Clamp valores para o range válido de áudio (-1.0 a 1.0)
1050
+ safeBuffer[i] = Math.max(-1, Math.min(1.0, value));
1051
+ }
1052
+ }
1053
+ if (hasInvalidValues) {
1054
+ console.warn(`MP3 Encoder: Found invalid values (NaN/Infinity) in channel ${channelIndex}. ` +
1055
+ `Replaced with 0. Buffer length: ${buffer.length}`);
1056
+ }
1057
+ return safeBuffer;
1058
+ });
1059
+ try {
1060
+ this.encoder.encode(safeBuffers);
1061
+ // Contar buffers processados para garantir que há dados antes de finalizar
1062
+ this.bufferCount++;
1063
+ this.totalSamples += safeBuffers[0].length;
1064
+ }
1065
+ catch (error) {
1066
+ // Melhorar mensagem de erro para incluir informações de debug
1067
+ const errorMsg = error instanceof Error ? error.message : String(error);
1068
+ throw new Error(`MP3 encoding error: ${errorMsg}. ` +
1069
+ `Buffers: ${buffers.length} channels, lengths: ${buffers.map(b => b.length).join(', ')}, ` +
1070
+ `Total buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}`);
1071
+ }
864
1072
  }
865
1073
  /**
866
1074
  * Finaliza o encoding e retorna o Blob MP3
@@ -872,7 +1080,25 @@ class Mp3LameEncoderWrapper {
872
1080
  if (!this.encoder) {
873
1081
  throw new Error('Encoder is not initialized');
874
1082
  }
875
- return this.encoder.finish(mimeType);
1083
+ // Verificar se há dados processados
1084
+ if (this.bufferCount === 0) {
1085
+ console.warn('MP3 Encoder: finish() called but no buffers were encoded. This may cause issues with the Emscripten encoder.');
1086
+ // Ainda tentar finalizar, mas avisar
1087
+ }
1088
+ try {
1089
+ const blob = this.encoder.finish(mimeType);
1090
+ // Validar que o blob não está vazio
1091
+ if (blob.size === 0) {
1092
+ console.warn('MP3 Encoder: finish() returned empty blob. This may indicate insufficient audio data was encoded.');
1093
+ }
1094
+ return blob;
1095
+ }
1096
+ catch (error) {
1097
+ const errorMsg = error instanceof Error ? error.message : String(error);
1098
+ throw new Error(`MP3 finish() error: ${errorMsg}. ` +
1099
+ `Buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}, ` +
1100
+ `Sample rate: ${this.sampleRate}, Channels: ${this.numChannels}, Bitrate: ${this.bitrate}`);
1101
+ }
876
1102
  }
877
1103
  /**
878
1104
  * Cancela o encoding
@@ -882,6 +1108,9 @@ class Mp3LameEncoderWrapper {
882
1108
  this.encoder.cancel();
883
1109
  this.encoder = null;
884
1110
  }
1111
+ // Reset contadores
1112
+ this.bufferCount = 0;
1113
+ this.totalSamples = 0;
885
1114
  }
886
1115
  }
887
1116
  /**
package/dist/index.umd.js CHANGED
@@ -103,14 +103,36 @@
103
103
  }
104
104
  try {
105
105
  const inputBuffer = event.inputBuffer;
106
+ const actualChannels = inputBuffer.numberOfChannels;
106
107
  const buffers = [];
107
- // Extrair dados de cada canal
108
- for (let channel = 0; channel < this.numChannels; channel++) {
108
+ // Extrair dados de cada canal disponível
109
+ for (let channel = 0; channel < actualChannels; channel++) {
109
110
  const channelData = inputBuffer.getChannelData(channel);
110
111
  buffers.push(new Float32Array(channelData));
111
112
  }
113
+ // Se o número de canais do buffer não corresponde ao esperado, ajustar
114
+ if (actualChannels !== this.numChannels) {
115
+ if (actualChannels === 1 && this.numChannels === 2) {
116
+ // Mono -> Estéreo: duplicar o canal
117
+ buffers.push(new Float32Array(buffers[0]));
118
+ }
119
+ else if (actualChannels === 2 && this.numChannels === 1) {
120
+ // Estéreo -> Mono: usar apenas o primeiro canal
121
+ buffers.splice(1);
122
+ }
123
+ else {
124
+ // Outros casos: usar apenas os canais disponíveis até o limite esperado
125
+ while (buffers.length < this.numChannels && buffers.length > 0) {
126
+ // Duplicar o último canal se necessário
127
+ buffers.push(new Float32Array(buffers[buffers.length - 1]));
128
+ }
129
+ if (buffers.length > this.numChannels) {
130
+ buffers.splice(this.numChannels);
131
+ }
132
+ }
133
+ }
112
134
  // Codificar os dados
113
- if (this.encoder) {
135
+ if (this.encoder && buffers.length > 0) {
114
136
  this.encoder.encode(buffers);
115
137
  }
116
138
  }
@@ -151,6 +173,9 @@
151
173
  if (!this.encoder) {
152
174
  throw new Error('Encoder is not initialized');
153
175
  }
176
+ // Aguardar um pouco para garantir que todos os callbacks de áudio foram processados
177
+ // Isso evita condições de corrida onde finish() é chamado antes de todos os buffers serem processados
178
+ await new Promise(resolve => setTimeout(resolve, 50));
154
179
  const blob = this.encoder.finish(mimeType);
155
180
  const url = URL.createObjectURL(blob);
156
181
  const timecode = Date.now() - this.startTime;
@@ -647,15 +672,43 @@
647
672
  */
648
673
  constructor(sampleRate, numChannels, options = {}) {
649
674
  this.encoder = null;
675
+ this.bufferCount = 0;
676
+ this.totalSamples = 0;
677
+ // Validar parâmetros
678
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0) {
679
+ throw new Error(`Invalid sampleRate: ${sampleRate}. Must be a positive number.`);
680
+ }
681
+ if (!Number.isInteger(numChannels) || numChannels < 1 || numChannels > 2) {
682
+ throw new Error(`Invalid numChannels: ${numChannels}. Must be 1 (mono) or 2 (stereo).`);
683
+ }
650
684
  this.sampleRate = sampleRate;
651
685
  this.numChannels = numChannels;
652
- this.quality = options.quality ?? 0.5;
686
+ // Validar e limitar qualidade (-0.1 a 1.0 para Vorbis)
687
+ const rawQuality = options.quality ?? 0.5;
688
+ if (!Number.isFinite(rawQuality)) {
689
+ console.warn(`Invalid quality value: ${rawQuality}. Using default 0.5`);
690
+ this.quality = 0.5;
691
+ }
692
+ else {
693
+ // Clamp quality to valid range
694
+ this.quality = Math.max(-0.1, Math.min(1.0, rawQuality));
695
+ if (rawQuality !== this.quality) {
696
+ console.warn(`Quality value ${rawQuality} clamped to valid range: ${this.quality}`);
697
+ }
698
+ }
653
699
  // Verificar se OggVorbisEncoder está disponível
654
700
  if (typeof OggVorbisEncoder === 'undefined') {
655
701
  throw new Error('OggVorbisEncoder is not loaded. Make sure to load OggVorbisEncoder.min.js before using this encoder.');
656
702
  }
657
- // Criar instância do encoder
658
- this.encoder = new OggVorbisEncoder(sampleRate, numChannels, this.quality);
703
+ try {
704
+ // Criar instância do encoder
705
+ this.encoder = new OggVorbisEncoder(sampleRate, numChannels, this.quality);
706
+ }
707
+ catch (error) {
708
+ const errorMsg = error instanceof Error ? error.message : String(error);
709
+ throw new Error(`Failed to initialize OGG encoder: ${errorMsg}. ` +
710
+ `Parameters: sampleRate=${sampleRate}, numChannels=${numChannels}, quality=${this.quality}`);
711
+ }
659
712
  }
660
713
  /**
661
714
  * Codifica buffers de áudio
@@ -669,7 +722,60 @@
669
722
  if (buffers.length !== this.numChannels) {
670
723
  throw new Error(`Expected ${this.numChannels} channels, got ${buffers.length}`);
671
724
  }
672
- this.encoder.encode(buffers);
725
+ // Validar que todos os buffers têm o mesmo tamanho
726
+ if (buffers.length > 0) {
727
+ const expectedLength = buffers[0].length;
728
+ for (let i = 1; i < buffers.length; i++) {
729
+ if (buffers[i].length !== expectedLength) {
730
+ throw new Error(`Channel ${i} has length ${buffers[i].length}, expected ${expectedLength}`);
731
+ }
732
+ }
733
+ // Validar que há dados para processar
734
+ if (expectedLength === 0) {
735
+ // Buffer vazio, não há nada para codificar
736
+ return;
737
+ }
738
+ }
739
+ else {
740
+ // Nenhum buffer fornecido
741
+ return;
742
+ }
743
+ // Criar cópias dos buffers e validar valores (NaN, Infinity)
744
+ const safeBuffers = buffers.map((buffer, channelIndex) => {
745
+ const safeBuffer = new Float32Array(buffer.length);
746
+ let hasInvalidValues = false;
747
+ for (let i = 0; i < buffer.length; i++) {
748
+ const value = buffer[i];
749
+ // Verificar NaN e Infinity
750
+ if (!Number.isFinite(value)) {
751
+ hasInvalidValues = true;
752
+ // Substituir valores inválidos por 0
753
+ safeBuffer[i] = 0;
754
+ }
755
+ else {
756
+ // Clamp valores para o range válido de áudio (-1.0 a 1.0)
757
+ safeBuffer[i] = Math.max(-1, Math.min(1.0, value));
758
+ }
759
+ }
760
+ if (hasInvalidValues) {
761
+ console.warn(`OGG Encoder: Found invalid values (NaN/Infinity) in channel ${channelIndex}. ` +
762
+ `Replaced with 0. Buffer length: ${buffer.length}`);
763
+ }
764
+ return safeBuffer;
765
+ });
766
+ try {
767
+ this.encoder.encode(safeBuffers);
768
+ // Contar buffers processados para garantir que há dados antes de finalizar
769
+ this.bufferCount++;
770
+ this.totalSamples += safeBuffers[0].length;
771
+ }
772
+ catch (error) {
773
+ // Melhorar mensagem de erro para incluir informações de debug
774
+ const errorMsg = error instanceof Error ? error.message : String(error);
775
+ throw new Error(`OGG encoding error: ${errorMsg}. ` +
776
+ `Buffers: ${buffers.length} channels, lengths: ${buffers.map(b => b.length).join(', ')}, ` +
777
+ `Total buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}`);
778
+ }
673
779
  }
674
780
  /**
675
781
  * Finaliza o encoding e retorna o Blob OGG
@@ -681,7 +787,25 @@
681
787
  if (!this.encoder) {
682
788
  throw new Error('Encoder is not initialized');
683
789
  }
684
- return this.encoder.finish(mimeType);
790
+ // Verificar se há dados processados
791
+ if (this.bufferCount === 0) {
792
+ console.warn('OGG Encoder: finish() called but no buffers were encoded. This may cause issues with the Emscripten encoder.');
793
+ // Ainda tentar finalizar, mas avisar
794
+ }
795
+ try {
796
+ const blob = this.encoder.finish(mimeType);
797
+ // Validar que o blob não está vazio
798
+ if (blob.size === 0) {
799
+ console.warn('OGG Encoder: finish() returned empty blob. This may indicate insufficient audio data was encoded.');
800
+ }
801
+ return blob;
802
+ }
803
+ catch (error) {
804
+ const errorMsg = error instanceof Error ? error.message : String(error);
805
+ throw new Error(`OGG finish() error: ${errorMsg}. ` +
806
+ `Buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}, ` +
807
+ `Sample rate: ${this.sampleRate}, Channels: ${this.numChannels}, Quality: ${this.quality}`);
808
+ }
685
809
  }
686
810
  /**
687
811
  * Cancela o encoding
@@ -691,6 +815,9 @@
691
815
  this.encoder.cancel();
692
816
  this.encoder = null;
693
817
  }
818
+ // Reset contadores
819
+ this.bufferCount = 0;
820
+ this.totalSamples = 0;
694
821
  }
695
822
  }
696
823
  /**
@@ -845,15 +972,43 @@
845
972
  */
846
973
  constructor(sampleRate, numChannels, options = {}) {
847
974
  this.encoder = null;
975
+ this.bufferCount = 0;
976
+ this.totalSamples = 0;
977
+ // Validar parâmetros
978
+ if (!Number.isFinite(sampleRate) || sampleRate <= 0) {
979
+ throw new Error(`Invalid sampleRate: ${sampleRate}. Must be a positive number.`);
980
+ }
981
+ if (!Number.isInteger(numChannels) || numChannels < 1 || numChannels > 2) {
982
+ throw new Error(`Invalid numChannels: ${numChannels}. Must be 1 (mono) or 2 (stereo).`);
983
+ }
848
984
  this.sampleRate = sampleRate;
849
985
  this.numChannels = numChannels;
850
- this.bitrate = options.bitrate ?? 128;
986
+ // Validar e limitar bitrate (32 a 320 kbps para MP3)
987
+ const rawBitrate = options.bitrate ?? 128;
988
+ if (!Number.isFinite(rawBitrate) || !Number.isInteger(rawBitrate)) {
989
+ console.warn(`Invalid bitrate value: ${rawBitrate}. Using default 128`);
990
+ this.bitrate = 128;
991
+ }
992
+ else {
993
+ // Clamp bitrate to valid range
994
+ this.bitrate = Math.max(32, Math.min(320, rawBitrate));
995
+ if (rawBitrate !== this.bitrate) {
996
+ console.warn(`Bitrate value ${rawBitrate} clamped to valid range: ${this.bitrate}`);
997
+ }
998
+ }
851
999
  // Verificar se Mp3LameEncoder está disponível
852
1000
  if (typeof Mp3LameEncoder === 'undefined') {
853
1001
  throw new Error('Mp3LameEncoder is not loaded. Make sure to load Mp3LameEncoder.min.js before using this encoder.');
854
1002
  }
855
- // Criar instância do encoder
856
- this.encoder = new Mp3LameEncoder(sampleRate, numChannels, this.bitrate);
1003
+ try {
1004
+ // Criar instância do encoder
1005
+ this.encoder = new Mp3LameEncoder(sampleRate, numChannels, this.bitrate);
1006
+ }
1007
+ catch (error) {
1008
+ const errorMsg = error instanceof Error ? error.message : String(error);
1009
+ throw new Error(`Failed to initialize MP3 encoder: ${errorMsg}. ` +
1010
+ `Parameters: sampleRate=${sampleRate}, numChannels=${numChannels}, bitrate=${this.bitrate}`);
1011
+ }
857
1012
  }
858
1013
  /**
859
1014
  * Codifica buffers de áudio
@@ -867,7 +1022,60 @@
867
1022
  if (buffers.length !== this.numChannels) {
868
1023
  throw new Error(`Expected ${this.numChannels} channels, got ${buffers.length}`);
869
1024
  }
870
- this.encoder.encode(buffers);
1025
+ // Validar que todos os buffers têm o mesmo tamanho
1026
+ if (buffers.length > 0) {
1027
+ const expectedLength = buffers[0].length;
1028
+ for (let i = 1; i < buffers.length; i++) {
1029
+ if (buffers[i].length !== expectedLength) {
1030
+ throw new Error(`Channel ${i} has length ${buffers[i].length}, expected ${expectedLength}`);
1031
+ }
1032
+ }
1033
+ // Validar que há dados para processar
1034
+ if (expectedLength === 0) {
1035
+ // Buffer vazio, não há nada para codificar
1036
+ return;
1037
+ }
1038
+ }
1039
+ else {
1040
+ // Nenhum buffer fornecido
1041
+ return;
1042
+ }
1043
+ // Criar cópias dos buffers e validar valores (NaN, Infinity)
1044
+ const safeBuffers = buffers.map((buffer, channelIndex) => {
1045
+ const safeBuffer = new Float32Array(buffer.length);
1046
+ let hasInvalidValues = false;
1047
+ for (let i = 0; i < buffer.length; i++) {
1048
+ const value = buffer[i];
1049
+ // Verificar NaN e Infinity
1050
+ if (!Number.isFinite(value)) {
1051
+ hasInvalidValues = true;
1052
+ // Substituir valores inválidos por 0
1053
+ safeBuffer[i] = 0;
1054
+ }
1055
+ else {
1056
+ // Clamp valores para o range válido de áudio (-1.0 a 1.0)
1057
+ safeBuffer[i] = Math.max(-1, Math.min(1.0, value));
1058
+ }
1059
+ }
1060
+ if (hasInvalidValues) {
1061
+ console.warn(`MP3 Encoder: Found invalid values (NaN/Infinity) in channel ${channelIndex}. ` +
1062
+ `Replaced with 0. Buffer length: ${buffer.length}`);
1063
+ }
1064
+ return safeBuffer;
1065
+ });
1066
+ try {
1067
+ this.encoder.encode(safeBuffers);
1068
+ // Contar buffers processados para garantir que há dados antes de finalizar
1069
+ this.bufferCount++;
1070
+ this.totalSamples += safeBuffers[0].length;
1071
+ }
1072
+ catch (error) {
1073
+ // Melhorar mensagem de erro para incluir informações de debug
1074
+ const errorMsg = error instanceof Error ? error.message : String(error);
1075
+ throw new Error(`MP3 encoding error: ${errorMsg}. ` +
1076
+ `Buffers: ${buffers.length} channels, lengths: ${buffers.map(b => b.length).join(', ')}, ` +
1077
+ `Total buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}`);
1078
+ }
871
1079
  }
872
1080
  /**
873
1081
  * Finaliza o encoding e retorna o Blob MP3
@@ -879,7 +1087,25 @@
879
1087
  if (!this.encoder) {
880
1088
  throw new Error('Encoder is not initialized');
881
1089
  }
882
- return this.encoder.finish(mimeType);
1090
+ // Verificar se há dados processados
1091
+ if (this.bufferCount === 0) {
1092
+ console.warn('MP3 Encoder: finish() called but no buffers were encoded. This may cause issues with the Emscripten encoder.');
1093
+ // Ainda tentar finalizar, mas avisar
1094
+ }
1095
+ try {
1096
+ const blob = this.encoder.finish(mimeType);
1097
+ // Validar que o blob não está vazio
1098
+ if (blob.size === 0) {
1099
+ console.warn('MP3 Encoder: finish() returned empty blob. This may indicate insufficient audio data was encoded.');
1100
+ }
1101
+ return blob;
1102
+ }
1103
+ catch (error) {
1104
+ const errorMsg = error instanceof Error ? error.message : String(error);
1105
+ throw new Error(`MP3 finish() error: ${errorMsg}. ` +
1106
+ `Buffers processed: ${this.bufferCount}, Total samples: ${this.totalSamples}, ` +
1107
+ `Sample rate: ${this.sampleRate}, Channels: ${this.numChannels}, Bitrate: ${this.bitrate}`);
1108
+ }
883
1109
  }
884
1110
  /**
885
1111
  * Cancela o encoding
@@ -889,6 +1115,9 @@
889
1115
  this.encoder.cancel();
890
1116
  this.encoder = null;
891
1117
  }
1118
+ // Reset contadores
1119
+ this.bufferCount = 0;
1120
+ this.totalSamples = 0;
892
1121
  }
893
1122
  }
894
1123
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-audio-recorder-ts",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "TypeScript port of web-audio-recorder-js with full type support for WAV, OGG Vorbis, and MP3 audio recording",
6
6
  "main": "dist/index.cjs.js",