

# SDK di trasmissione IVS: sorgenti audio personalizzate \| Streaming in tempo reale
<a name="broadcast-custom-audio-sources"></a>

**Nota:** questa guida è valida solo per l'SDK di trasmissione Android per lo streaming in tempo reale IVS. Le informazioni relative agli SDK per le versioni web e iOS verranno pubblicate in futuro.

Le sorgenti audio personalizzate consentono a un'applicazione di fornire il proprio input audio all'SDK di trasmissione, anziché limitarsi al microfono integrato del dispositivo. Una sorgente audio personalizzata consente alle applicazioni di trasmettere contenuti audio elaborati con effetti, mixare più flussi audio o effettuare l'integrazione con librerie di elaborazione audio di terzi.

Quando utilizzi una sorgente audio personalizzata, l'SDK di trasmissione non sarà più direttamente responsabile della gestione del microfono. L'applicazione sarà invece responsabile dell'acquisizione, dell'elaborazione e dell'invio dei dati audio alla sorgente personalizzata.

Il flusso di lavoro relativo alla sorgente audio personalizzata segue i passaggi descritti di seguito:

1. Input audio: crea una sorgente audio personalizzata con un formato audio specificato (frequenza di campionamento, canali, formato). 

1. Elaborazione: acquisisci o genera dati audio dalla pipeline di elaborazione audio.

1. Sorgente audio personalizzata: invia i buffer audio alla sorgente personalizzata utilizzando `appendBuffer()`.

1. Fase: completa il `LocalStageStream` e pubblicalo nella fase utilizzando la `StageStrategy`. 

1. Partecipanti: i partecipanti alla fase ricevono l'audio elaborato in tempo reale.

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

### Creazione di una sorgente audio personalizzata
<a name="custom-audio-sources-android-creating-a-custom-audio-source"></a>

Dopo aver creato una sessione di `DeviceDiscovery`, crea un input per una sorgente audio personalizzata:

```
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) 
);
```

Questo metodo restituisce una `CustomAudioSource`, che accetta dati audio PCM non elaborati. La sorgente audio personalizzata deve essere configurata con lo stesso formato audio prodotto dalla pipeline di elaborazione audio.

#### Formati audio supportati
<a name="custom-audio-sources-android-submitting-audio-data-supportedi-audio-formats"></a>


| Parametro | Opzioni | Descrizione | 
| --- | --- | --- | 
| Canali | 1 (mono), 2 (stereo) | Numero di canali audio. | 
| Frequenza di campionamento | RATE\_16000, RATE\_44100, RATE\_48000 | Frequenza di campionamento audio in Hz. Si consiglia di selezionare 48 kHz per una qualità elevata. | 
| Formato | INT16, FLOAT32 | Formato audio di esempio. INT16 è un PCM a virgola fissa a 16 bit, FLOAT32 è un PCM a virgola mobile a 32 bit. Sono disponibili sia i formati interlacciati che quelli planari. | 

### Invio dei dati audio
<a name="custom-audio-sources-android-submitting-audio-data"></a>

Per inviare dati audio alla sorgente personalizzata, utilizza il metodo `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();
```

**Considerazioni importanti:**
+ I dati audio devono essere nel formato specificato durante la creazione della sorgente personalizzata.
+ I timestamp devono aumentare in modo monotono e devono essere forniti dalla sorgente audio per una riproduzione audio fluida.
+ Invia l'audio regolarmente per evitare interruzioni nello streaming.
+ Il metodo restituisce il numero di campioni elaborati (0 indica un errore). 

### Pubblicazione in una fase
<a name="custom-audio-sources-android-publishing-to-a-stage"></a>

Completa la `CustomAudioSource` in uno `AudioLocalStageStream` e restituisci questo elemento dalla `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);
```

### Esempio completo: integrazione dell'elaborazione audio
<a name="custom-audio-sources-android-complete-example"></a>

Ecco un esempio completo che mostra l'integrazione con un SDK di elaborazione 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(); 
      } 
   } 
}
```

### Best practice
<a name="custom-audio-sources-android-best-practices"></a>

#### Uniformità del formato audio
<a name="custom-audio-sources-android-best-practices-audio-format-consistency"></a>

Assicurati che il formato audio inviato corrisponda al formato specificato durante la creazione della sorgente personalizzata:

```
// 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
```

#### Gestione dei buffer
<a name="custom-audio-sources-android-best-practices-buffer-managemetn"></a>

Utilizzali i `ByteBuffers` diretti e riutilizzali per ridurre al minimo la rimozione di oggetti inutili: 

```
// 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(); 
}
```

#### Tempistica e sincronizzazione
<a name="custom-audio-sources-android-best-practices-timing-and-synchronization"></a>

È necessario utilizzare i timestamp forniti dalla sorgente audio per una riproduzione audio fluida. Se la sorgente audio non fornisce un timestamp, crea il timestamp epoch e calcola manualmente la durata tra un campione e l'altro utilizzando il numero di fotogrammi e la dimensione dei fotogrammi. 

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

#### Gestione errori
<a name="custom-audio-sources-android-best-practices-error-handling"></a>

Controlla sempre il valore restituito da `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. 
}
```