write me a c# example of how to inference into this uvicorn http server, keep it simple.

import os
import torch
import torchaudio
from fastapi import FastAPI, UploadFile, File
from fastapi.responses import StreamingResponse
import numpy as np
from pydub import AudioSegment
from pathlib import Path
import yaml
from modules.commons import build_model, load_checkpoint, recursive_munch, str2bool
from hf_utils import load_custom_model_from_hf

# Initialize FastAPI
app = FastAPI()

# Load model and configuration globally
fp16 = False
device = None

def load_models(args):
    global sr, hop_length, fp16
    fp16 = args.fp16
    ckpt_root = Path(__file__).parent / "checkpoints"
    print(f"Using device: {device}")
    print(f"Using fp16: {fp16}")

    # Load your model checkpoint and configuration here (placeholder)
    # You will need to adapt this code to match your models
    # Example: loading a model and printing some info
    # model = some_model_loading_function(args.checkpoint_path)
    
    return {
        # Returning the required components for voice conversion
        "model": None,  # Replace None with the actual model variable
        "semantic_fn": None,
        "vocoder_fn": None,
        "campplus_model": None,
        "to_mel": None,
        "mel_fn_args": None,
        "f0_fn": None,
    }

# Define global variables for models and parameters
model_f0, semantic_fn, vocoder_fn, campplus_model, to_mel_f0, mel_fn_args = (None,)*6
f0_fn = None
sr, hop_length = None, None

@torch.no_grad()
@torch.inference_mode()
def voice_conversion(source_audio_path, target_audio_path, diffusion_steps, length_adjust, inference_cfg_rate, auto_f0_adjust, pitch_shift):
    # Implement the actual voice conversion logic here
    # Example placeholder; replace with your actual implementation
    print(f"Processing: {source_audio_path} and {target_audio_path}")
    audio_bytes = b''  # Replace with actual audio processing result (byte stream)
    full_output = None  # Replace with your full output if necessary

    # Return processed audio (placeholder)
    return audio_bytes, full_output

@app.post("/voice_conversion/")
async def convert_voice(
    source: UploadFile = File(...),
    target: UploadFile = File(...),
    diffusion_steps: int = 10,
    length_adjust: float = 1.0,
    inference_cfg_rate: float = 0.7,
    auto_f0_adjust: bool = True,
    pitch_shift: int = 0
):
    # Handle uploaded source and target files
    source_audio_path = f"temp/{source.filename}"
    target_audio_path = f"temp/{target.filename}"

    os.makedirs("temp", exist_ok=True)  # Create temp directory if it doesn't exist

    # Save the uploaded files temporarily
    with open(source_audio_path, "wb") as buffer:
        buffer.write(await source.read())

    with open(target_audio_path, "wb") as buffer:
        buffer.write(await target.read())

    # Call the voice conversion function
    audio_bytes, full_output = voice_conversion(
        source_audio_path,
        target_audio_path,
        diffusion_steps,
        length_adjust,
        inference_cfg_rate,
        auto_f0_adjust,
        pitch_shift
    )

    # Return the audio as StreamingResponse
    return StreamingResponse(iter([audio_bytes]), media_type="audio/mp3")

def main(args):
    global model_f0, semantic_fn, vocoder_fn, campplus_model, to_mel_f0, mel_fn_args, f0_fn
    model_parameters = load_models(args)
    model_f0 = model_parameters["model"]
    semantic_fn = model_parameters["semantic_fn"]
    vocoder_fn = model_parameters["vocoder_fn"]
    campplus_model = model_parameters["campplus_model"]
    to_mel_f0 = model_parameters["to_mel"]
    mel_fn_args = model_parameters["mel_fn_args"]
    f0_fn = model_parameters["f0_fn"]

if __name__ == "__main__":
    import argparse
    # Argument parsing for model config paths and device configuration
    parser = argparse.ArgumentParser()
    parser.add_argument("--checkpoint-path", type=str, help="Path to the checkpoint file", default=None)
    parser.add_argument("--config-path", type=str, help="Path to the config file", default=None)
    parser.add_argument("--fp16", type=str2bool, nargs="?", const=True, help="Whether to use fp16", default=True)
    parser.add_argument("--gpu", type=int, help="Which GPU id to use", default=0)
    args = parser.parse_args()
    cuda_target = f"cuda:{args.gpu}" if args.gpu else "cuda"
    device = torch.device(cuda_target if torch.cuda.is_available() else "cpu")
    main(args)

    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)