

# Kit SDK de diffusion IVS : Sources audio personnalisées \$1 Diffusion en temps réel
<a name="broadcast-custom-audio-sources"></a>

**Remarque :** Ce guide s'applique uniquement au SDK de diffusion Android pour le streaming en temps réel IVS. Les informations relatives aux SDK iOS et Web seront publiées à l’avenir.

Les sources d’entrée audio personnalisées permettent à une application de fournir sa propre entrée audio au SDK de diffusion, au lieu de se limiter au microphone intégré de l’appareil. Une source audio personnalisée permet aux applications de diffuser de l’audio traité avec des effets, de mixer plusieurs flux audio ou de les intégrer à des bibliothèques de traitement audio tierces.

Lorsque vous utilisez une source d’entrée audio personnalisée, le SDK de diffusion n’est plus responsable de la gestion directe du microphone. Au lieu de cela, votre application est chargée de capturer, traiter et envoyer les données audio à la source personnalisée.

Le flux de travail des sources audio personnalisées suit ces étapes :

1. Entrée audio : créez une source audio personnalisée avec le format audio spécifié (fréquence d’échantillonnage, canaux, format). 

1. Votre traitement : capturez ou générez des données audio à partir de votre pipeline de traitement audio.

1. Source audio personnalisée : soumettez des tampons audio à la source personnalisée avec `appendBuffer()`.

1. Étape : encapsulez dans `LocalStageStream` et publiez sur l’étape via votre `StageStrategy`. 

1. Participants : les participants à l’étape reçoivent le son traité en temps réel.

## Android
<a name="custom-audio-sources-android"></a>

### Création d’une source audio personnalisée
<a name="custom-audio-sources-android-creating-a-custom-audio-source"></a>

Après avoir créé une session `DeviceDiscovery`, créez une source d’entrée audio personnalisée :

```
DeviceDiscovery deviceDiscovery = new DeviceDiscovery(context); 
 
// Create custom audio source with specific format 
CustomAudioSource customAudioSource = deviceDiscovery.createAudioInputSource( 
   2,  // Number of channels (1 = mono, 2 = stereo) 
   BroadcastConfiguration.AudioSampleRate.RATE_48000,  // Sample rate 
   AudioDevice.Format.INT16  // Audio format (16-bit PCM) 
);
```

Cette méthode renvoie une `CustomAudioSource`, qui accepte les données audio PCM brutes. La source audio personnalisée doit être configurée avec le même format audio que celui produit par votre pipeline de traitement audio.

#### Formats audio pris en charge
<a name="custom-audio-sources-android-submitting-audio-data-supportedi-audio-formats"></a>


| Paramètre | Options | Description | 
| --- | --- | --- | 
| Canaux | 1 (mono), 2 (stéréo) | Nombre de canaux audio. | 
| Fréquence d’échantillonnage | RATE\$116000, RATE\$144100, RATE\$148000 | Fréquence d’échantillonnage audio en Hz. Une valeur de 48 kHz est recommandée pour une haute qualité. | 
| Format | INT16, FLOAT32 | Format d’échantillonnage audio. INT16 est un PCM à virgule fixe 16 bits, FLOAT32 est un PCM à virgule flottante 32 bits. Les formats entrelacés et planaires sont disponibles. | 

### Envoi des données audio
<a name="custom-audio-sources-android-submitting-audio-data"></a>

Pour envoyer des données audio à la source personnalisée, utilisez la méthode `appendBuffer()` :

```
// Prepare audio data in a ByteBuffer 
ByteBuffer audioBuffer = ByteBuffer.allocateDirect(bufferSize); 
audioBuffer.put(pcmAudioData);  // Your processed audio data 
 
// Calculate the number of bytes 
long byteCount = pcmAudioData.length; 
 
// Submit audio to the custom source 
// presentationTimeUs should be generated by and come from your audio source
int samplesProcessed = customAudioSource.appendBuffer( 
   audioBuffer, 
   byteCount, 
   presentationTimeUs 
); 
 
if (samplesProcessed > 0) { 
   Log.d(TAG, "Successfully submitted " + samplesProcessed + " samples"); 
} else { 
   Log.w(TAG, "Failed to submit audio samples"); 
} 
 
// Clear buffer for reuse 
audioBuffer.clear();
```

**Considérations importantes:**
+ Les données audio doivent être au format spécifié lors de la création de la source personnalisée.
+ Les horodatages doivent augmenter de façon monotone et être fournis par votre source audio pour une lecture audio fluide.
+ Envoyez des données audio régulièrement pour éviter les interruptions dans le flux.
+ La méthode renvoie le nombre d’échantillons traités (0 indique un échec). 

### Publication sur une étape
<a name="custom-audio-sources-android-publishing-to-a-stage"></a>

Encapsulez la `CustomAudioSource` dans un `AudioLocalStageStream` et retournez-le dans votre `StageStrategy` :

```
// Create the audio stream from custom source 
AudioLocalStageStream audioStream = new AudioLocalStageStream(customAudioSource); 
 
// Define your stage strategy 
Strategy stageStrategy = new Strategy() { 
   @NonNull 
   @Override 
   public List<LocalStageStream> stageStreamsToPublishForParticipant( 
         @NonNull Stage stage, 
         @NonNull ParticipantInfo participantInfo) { 
      List<LocalStageStream> streams = new ArrayList<>(); 
      streams.add(audioStream);  // Publish custom audio 
      return streams; 
   } 
 
   @Override 
   public boolean shouldPublishFromParticipant( 
         @NonNull Stage stage, 
         @NonNull ParticipantInfo participantInfo) { 
      return true;  // Control when to publish 
   } 
 
   @Override 
   public Stage.SubscribeType shouldSubscribeToParticipant( 
         @NonNull Stage stage, 
         @NonNull ParticipantInfo participantInfo) { 
      return Stage.SubscribeType.AUDIO_VIDEO; 
   } 
}; 
 
// Create and join the stage 
Stage stage = new Stage(context, stageToken, stageStrategy);
```

### Exemple complet : intégration du traitement audio
<a name="custom-audio-sources-android-complete-example"></a>

Voici un exemple complet illustrant l’intégration avec un SDK de traitement audio :

```
public class AudioStreamingActivity extends AppCompatActivity { 
   private DeviceDiscovery deviceDiscovery; 
   private CustomAudioSource customAudioSource; 
   private AudioLocalStageStream audioStream; 
   private Stage stage; 
 
   @Override 
   protected void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
 
      // Configure audio manager 
      StageAudioManager.getInstance(this) 
         .setPreset(StageAudioManager.UseCasePreset.VIDEO_CHAT); 
 
      // Initialize IVS components 
      initializeIVSStage(); 
 
      // Initialize your audio processing SDK 
      initializeAudioProcessing(); 
   } 
 
   private void initializeIVSStage() { 
      deviceDiscovery = new DeviceDiscovery(this); 
 
      // Create custom audio source (48kHz stereo, 16-bit) 
      customAudioSource = deviceDiscovery.createAudioInputSource( 
         2,  // Stereo 
         BroadcastConfiguration.AudioSampleRate.RATE_48000, 
         AudioDevice.Format.INT16 
      ); 
 
      // Create audio stream 
      audioStream = new AudioLocalStageStream(customAudioSource); 
 
      // Create stage with strategy 
      Strategy strategy = new Strategy() { 
         @NonNull 
         @Override 
         public List<LocalStageStream> stageStreamsToPublishForParticipant( 
               @NonNull Stage stage, 
               @NonNull ParticipantInfo participantInfo) { 
            return Collections.singletonList(audioStream); 
         } 
 
         @Override 
         public boolean shouldPublishFromParticipant( 
               @NonNull Stage stage, 
               @NonNull ParticipantInfo participantInfo) { 
            return true; 
         } 
 
         @Override 
         public Stage.SubscribeType shouldSubscribeToParticipant( 
               @NonNull Stage stage, 
               @NonNull ParticipantInfo participantInfo) { 
            return Stage.SubscribeType.AUDIO_VIDEO; 
         } 
      }; 
 
      stage = new Stage(this, getStageToken(), strategy); 
   } 
 
   private void initializeAudioProcessing() { 
      // Initialize your audio processing SDK 
      // Set up callback to receive processed audio 
      yourAudioSDK.setAudioCallback(new AudioCallback() { 
         @Override 
         public void onProcessedAudio(byte[] audioData, int sampleRate, 
                                     int channels, long timestamp) { 
            // Submit processed audio to IVS Stage 
            submitAudioToStage(audioData, timestamp); 
         } 
      }); 
   } 
 
   // The timestamp is required to come from your audio source and you  
   // should not be generating one on your own, unless your audio source 
   // does not provide one. If that is the case, create your own epoch  
   // timestamp and manually calculate the duration between each sample  
   // using the number of frames and frame size. 

   private void submitAudioToStage(byte[] audioData, long timestamp) { 
      try { 
         // Allocate direct buffer 
         ByteBuffer buffer = ByteBuffer.allocateDirect(audioData.length); 
         buffer.put(audioData); 
 
         // Submit to custom audio source 
         int samplesProcessed = customAudioSource.appendBuffer( 
            buffer, 
            audioData.length, 
            timestamp > 0 ? timestamp : System.nanoTime() / 1000 
         ); 
 
         if (samplesProcessed <= 0) { 
            Log.w(TAG, "Failed to submit audio samples"); 
         } 
 
         buffer.clear(); 
      } catch (Exception e) { 
         Log.e(TAG, "Error submitting audio: " + e.getMessage(), e); 
      } 
   } 
 
   @Override 
   protected void onDestroy() { 
      super.onDestroy(); 
      if (stage != null) { 
          stage.release(); 
      } 
   } 
}
```

### Bonnes pratiques
<a name="custom-audio-sources-android-best-practices"></a>

#### Cohérence du format audio
<a name="custom-audio-sources-android-best-practices-audio-format-consistency"></a>

Assurez-vous que le format audio que vous envoyez correspond au format spécifié lors de la création de la source personnalisée :

```
// If you create with 48kHz stereo INT16 
customAudioSource = deviceDiscovery.createAudioInputSource( 
   2, RATE_48000, INT16 
); 
 
// Your audio data must be: 
// - 2 channels (stereo) 
// - 48000 Hz sample rate 
// - 16-bit interleaved PCM format
```

#### Gestion de la mémoire tampon
<a name="custom-audio-sources-android-best-practices-buffer-managemetn"></a>

Utilisez des `ByteBuffers` directs et réutilisez-les pour minimiser le travail du récupérateur de mémoire : 

```
// Allocate once 
private ByteBuffer audioBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE); 
 
// Reuse in callback 
public void onAudioData(byte[] data) { 
   audioBuffer.clear(); 
   audioBuffer.put(data); 
   customAudioSource.appendBuffer(audioBuffer, data.length, getTimestamp()); 
   audioBuffer.clear(); 
}
```

#### Timing et synchronisation
<a name="custom-audio-sources-android-best-practices-timing-and-synchronization"></a>

Vous devez utiliser les horodatages fournis par votre source audio pour une lecture audio fluide. Si votre source audio ne fournit pas son propre horodatage, créez votre propre horodatage d’époque et calculez manuellement la durée entre chaque échantillon en utilisant le nombre d’images et la taille d’image. 

```
// "audioFrameTimestamp" should be generated by your audio source
// Consult your audio source’s documentation for information on how to get this 
long timestamp = audioFrameTimestamp;
```

#### Gestion des erreurs
<a name="custom-audio-sources-android-best-practices-error-handling"></a>

Vérifiez toujours la valeur de retour de `appendBuffer()` : 

```
int samplesProcessed = customAudioSource.appendBuffer(buffer, count, timestamp); 
 
if (samplesProcessed <= 0) { 
   Log.w(TAG, "Audio submission failed - buffer may be full or format mismatch"); 
   // Handle error: check format, reduce submission rate, etc. 
}
```