I need to combine these 3 codes into one csv output

import torch
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
import torchaudio
from pyannote.audio import Pipeline  # Ensure you have Pyannote installed
from speechbrain.pretrained import foreign_class  # Ensure you have SpeechBrain installed
import logging
import pandas as pd
import os
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta

# Constants
RECORDINGS_DIR = r"X:\Bidvest_ASC_recordings\9247a83d-680b-4ecc-aa0e-40d03d60ae2a.AscVolume"  # Directory with recordings
SAMPLE_RATE = 16000  # Desired sample rate
CHUNK_LENGTH = 30    # Duration of each chunk in seconds
BATCH_SIZE = 1       # Set to 1 for simplicity when testing
OUTPUT_DIR = r"X:\Bidvest_ASC_recordings\segments"  # Directory to save audio segments
network_base_path = r"\\172.21.18.180\Bidvest_ASC_recordings"

# Set up logging
logging.basicConfig(level=logging.INFO)

# Load the Whisper model and processor
device = "cpu"  # Use CPU
torch_dtype = torch.float32  # Use float32 for CPU
model_id = "openai/whisper-large-v3-turbo"

model = AutoModelForSpeechSeq2Seq.from_pretrained(
    model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True
)
model.to(device)

processor = AutoProcessor.from_pretrained(model_id)

# Create the pipeline
pipe = pipeline(
    "automatic-speech-recognition",
    model=model,
    tokenizer=processor.tokenizer,
    feature_extractor=processor.feature_extractor,
    chunk_length_s=CHUNK_LENGTH,  # Use the chunked algorithm
    batch_size=BATCH_SIZE,        # Set batch size for inference
    torch_dtype=torch_dtype,
    device=device,
)

# Updated generate_kwargs for Whisper model with additional hyperparameters
generate_kwargs = {
    "max_new_tokens": 300,                  # Increase the maximum tokens to capture longer transcriptions
    "num_beams": 5,                         # Use beam search with 5 beams for a balance between accuracy and speed
    "return_timestamps": "word",            # Return word-level timestamps
    "language": "english"
}

# Function to load and preprocess the audio file
def load_and_resample_audio(file_path, sample_rate):
    waveform, original_sample_rate = torchaudio.load(file_path)
    if waveform.size(0) > 1:
        waveform = torch.mean(waveform, dim=0, keepdim=True)
    if original_sample_rate != sample_rate:
        resampler = torchaudio.transforms.Resample(orig_freq=original_sample_rate, new_freq=sample_rate)
        waveform = resampler(waveform)
    return waveform.squeeze(0)

# Load Pyannote Speaker Diarization Pipeline
logging.info("Loading Pyannote speaker diarization pipeline...")
hf_token = "hf_liKIskKwsaWRovlsemOvecQolMLjZMktV"  # Replace with your actual Hugging Face token
try:
    diarization_pipeline = Pipeline.from_pretrained(
        "pyannote/speaker-diarization-3.1",
        use_auth_token=hf_token
    )
    logging.info("Speaker diarization pipeline loaded successfully.")
except Exception as e:
    logging.error(f"Failed to load diarization pipeline: {e}")

# Load SpeechBrain Emotion Classifier
logging.info("Loading SpeechBrain emotion classifier...")
try:
    emotion_classifier = foreign_class(
        source="speechbrain/emotion-recognition-wav2vec2-IEMOCAP",
        pymodule_file="custom_interface.py",
        classname="CustomEncoderWav2vec2Classifier"
    )
    logging.info("Emotion classifier loaded successfully.")
except Exception as e:
    logging.error(f"Failed to load emotion classifier: {e}")

# Load Sentiment Analysis Pipeline
logging.info("Loading Sentiment Analysis pipeline...")
try:
    sentiment_analyzer = pipeline("text-classification", model="distilbert/distilbert-base-uncased-finetuned-sst-2-english", return_all_scores=True)
    logging.info("Sentiment Analysis pipeline loaded successfully.")
except Exception as e:
    logging.error(f"Failed to load Sentiment Analysis pipeline: {e}")

# XML RecKey Extraction
def get_rec_key(audio_file_path):
    xml_file_path = audio_file_path.replace('.wav', '.xml')
    tree = ET.parse(xml_file_path)
    root = tree.getroot()
    caller_number = root.findtext('.//callerNumber').strip()
    end_time_str = root.findtext('.//endtime').strip()
    end_time = datetime.strptime(end_time_str[:16], '%Y-%m-%dT%H:%M')
    adjusted_end_time = end_time + timedelta(hours=2)
    return f"{caller_number}{adjusted_end_time.strftime('%Y%m%d%H%M')}"

# Extract unique recording ID from the audio file path
def get_recording_id(file_path):
    filename = os.path.basename(file_path)
    return filename.split('.')[0]  # Assumes the ID is part of the filename before the extension

def save_audio_segment(waveform, start, end, speaker_label, segment_number, recording_id):
    segment_waveform = waveform[int(start * SAMPLE_RATE):int(end * SAMPLE_RATE)]
    segment_filename = os.path.join(
        OUTPUT_DIR, f"{recording_id}_speaker_{speaker_label}_segment_{segment_number}.wav"
    )
    torchaudio.save(segment_filename, segment_waveform.unsqueeze(0), SAMPLE_RATE)

    # Create a network path for the saved segment
    network_segment_path = segment_filename.replace(r"X:\Bidvest_ASC_recordings", network_base_path)
    return segment_filename, network_segment_path  # Return both local and network paths

# Function to classify emotion for a segment
def classify_emotion(segment_file):
    try:
        out_prob, score, index, emotion_label = emotion_classifier.classify_file(segment_file)
        return (emotion_label, score.item() if isinstance(score, torch.Tensor) else score)
    except Exception as e:
        logging.error(f"Error during emotion classification for {segment_file}: {e}")
        return (['unknown'], 0.0)

# Function to perform sentiment analysis on a segment
def classify_sentiment(text):
    try:
        sentiment_result = sentiment_analyzer(text)
        sentiment_scores = {sent['label']: sent['score'] for sent in sentiment_result[0]}
        return sentiment_scores
    except Exception as e:
        logging.error(f"Error during sentiment analysis: {e}")
        return {}

# Align Words with Diarization and perform sentiment analysis for each segment
def align_words_with_diarization(diarization_result, word_timestamps, recording_id, rec_key, waveform, sentiment_scores_full):
    aligned_script = []
    segment_number = 1
    diarized_segments = [
        (segment.start, segment.end, label)
        for segment, _, label in diarization_result.itertracks(yield_label=True)
    ]
    current_speaker = None
    current_segment_start = None
    current_segment_end = None
    current_words = []
    current_sentiment_scores = {}
    last_segment_end_time = 0  # Initialize the end time of the last segment to 0

    for word_info in word_timestamps:
        word = word_info['text']
        start_time = word_info['timestamp'][0]
        end_time = word_info['timestamp'][1]

        # Ensure start_time and end_time are valid numbers
        if start_time is None or end_time is None:
            logging.warning(f"Invalid timestamps for word '{word}': {word_info['timestamp']}")
            continue  # Skip to the next word

        for (seg_start, seg_end, speaker_label) in diarized_segments:
            if seg_start <= start_time <= seg_end:
                if current_speaker != speaker_label:
                    if current_speaker is not None:
                        # Save the current segment and perform sentiment analysis
                        segment_file, network_segment_path = save_audio_segment(
                            waveform, current_segment_start, current_segment_end, current_speaker, segment_number, recording_id
                        )
                        aligned_script.append({
                            'RecKey': rec_key,
                            'Start Time': current_segment_start,
                            'End Time': current_segment_end,
                            'Speaker': current_speaker,
                            'Transcribed Text': ' '.join(current_words),
                            'Order Number': segment_number,
                            'Emotional Class': classify_emotion(segment_file)[0],
                            'Emotional Class Score': classify_emotion(segment_file)[1],
                            **current_sentiment_scores,
                            'Segment Audio Hyperlink': network_segment_path
                        })
                        segment_number += 1

                    # Reset for the new speaker, and adjust start time to follow the last segment
                    current_speaker = speaker_label
                    current_segment_start = max(last_segment_end_time, start_time - 2)
                    current_segment_end = end_time + 2
                    current_words = [word]
                    current_sentiment_scores = classify_sentiment(' '.join(current_words))
                else:
                    # Extend end time and add the word to the current segment for continuity
                    current_segment_end = end_time + 2
                    current_words.append(word)
                    current_sentiment_scores = classify_sentiment(' '.join(current_words))

                last_segment_end_time = current_segment_end  # Update end time for the next iteration
                break

    # Final check for the last segment
    if current_speaker is not None and current_words:
        segment_file, network_segment_path = save_audio_segment(
            waveform, current_segment_start, current_segment_end, current_speaker, segment_number, recording_id
        )
        aligned_script.append({
            'RecKey': rec_key,
            'Start Time': current_segment_start,
            'End Time': current_segment_end,
            'Speaker': current_speaker,
            'Transcribed Text': ' '.join(current_words),
            'Order Number': segment_number,
            'Emotional Class': classify_emotion(segment_file)[0],
            'Emotional Class Score': classify_emotion(segment_file)[1],
            **current_sentiment_scores,
            'Segment Audio Hyperlink': network_segment_path
        })

    return pd.DataFrame(aligned_script)

# Loop over recordings in the directory
csv_full_transcription = 'full_transcription_results.csv'
csv_aligned_segments = 'aligned_segments_results.csv'
processed_files = 0

for file in os.listdir(RECORDINGS_DIR):
    if file.endswith(".wav"):
        audio_file_path = os.path.join(RECORDINGS_DIR, file)
        try:
            rec_key = get_rec_key(audio_file_path)
            recording_id = get_recording_id(audio_file_path)

            # Load and preprocess the audio
            waveform = load_and_resample_audio(audio_file_path, SAMPLE_RATE)
            if waveform.size(0) < SAMPLE_RATE * CHUNK_LENGTH:
                logging.warning(f"Audio is too short for processing: {file}")
                continue
            
            result = pipe(waveform.numpy(), return_timestamps="word", generate_kwargs=generate_kwargs)

            # Full transcription and word-level timestamps
            transcription_text = result['text']
            word_timestamps = result['chunks']

            # Speaker Diarization
            diarization_result = diarization_pipeline(audio_file_path)

            # Emotion Classification for Full Transcript
            emotion_label_full, emotion_score_full = classify_emotion(audio_file_path)

            # Sentiment Analysis on Full Transcript
            sentiment_scores_full = classify_sentiment(transcription_text)

            # Create DataFrame for Full Transcription
            network_path = audio_file_path.replace(r"X:\Bidvest_ASC_recordings", network_base_path)
            df_full_transcription = pd.DataFrame({
                'RecKey': [rec_key],
                'Full Transcription': [transcription_text],
                'Emotion Classification': [emotion_label_full],
                'Emotion Score': [emotion_score_full],
                'Network Hyperlink': [network_path],
                **sentiment_scores_full
            })

            if os.path.exists(csv_full_transcription):
                df_full_transcription.to_csv(csv_full_transcription, mode='a', header=False, index=False)
            else:
                df_full_transcription.to_csv(csv_full_transcription, index=False)

            # Align words with diarization results and perform sentiment analysis for each segment
            df_aligned_segments = align_words_with_diarization(diarization_result, word_timestamps, recording_id, rec_key, waveform, sentiment_scores_full)

            if os.path.exists(csv_aligned_segments):
                df_aligned_segments.to_csv(csv_aligned_segments, mode='a', header=False, index=False)
            else:
                df_aligned_segments.to_csv(csv_aligned_segments, index=False)

            processed_files += 1
            if processed_files >= 50000000:
                break

        except Exception as e:
            logging.error(f"Error processing file '{file}': {e}")
            continue  # Continue processing the next file

logging.info(f"Processed {processed_files} recordings.")



Part 2:

import pandas as pd
import logging
from transformers import pipeline

# Initialize logging
logging.basicConfig(level=logging.INFO)

# Load the CSV file into a DataFrame
file_path = r'C:\Users\FourieH\full_transcription_results.csv'
df = pd.read_csv(file_path)

# Correct the network hyperlink paths
df['Network Hyperlink'] = df['Network Hyperlink'].str.replace(
    r'^\\\\172\.21\.18\.180\\', 
    r'\\\\172.21.18.180\\CallCentreAudioFiles\\', 
    regex=True
)

# Load models
models = {
    "twitter_roberta_latest": "cardiffnlp/twitter-roberta-base-sentiment-latest",
    "distilbert_multilingual": "lxyuan/distilbert-base-multilingual-cased-sentiments-student",
    "amazon_review": "LiYuan/amazon-review-sentiment-analysis",
    "twitter_xlm_fine": "cardiffnlp/twitter-xlm-roberta-base-sentiment-multilingual"
}

# Initialize sentiment columns in DataFrame
for model_name in models.keys():
    df[f'Positive-{model_name}'] = None
    df[f'Negative-{model_name}'] = None
    df[f'Neutral-{model_name}'] = None
    # Additional columns for star ratings if applicable
    df[f'1 Star-{model_name}'] = None
    df[f'2 Star-{model_name}'] = None
    df[f'3 Star-{model_name}'] = None
    df[f'4 Star-{model_name}'] = None
    df[f'5 Star-{model_name}'] = None

# Add sentiment analysis columns dynamically
for model_name, model_path in models.items():
    logging.info(f"Loading {model_name} model...")
    analyzer = pipeline("text-classification", model=model_path, return_all_scores=True)

    for i, row in df.iterrows():
        rec_key = row["RecKey"]
        transcription = row["Full Transcription"]

        # Analyze sentiment with truncation and padding enabled
        try:
            result = analyzer(transcription, truncation=True, padding=True, max_length=490)
            logging.info(f"Result from {model_name} model for RecKey {rec_key}: {result}")

            # The result contains scores for all labels
            if isinstance(result, list) and len(result) > 0:
                for score_info in result[0]:  # Assuming the result is a list of dictionaries
                    label = score_info['label'].lower()  # Normalize the label to lower case
                    score = score_info['score']

                    # Populate DataFrame based on the label returned
                    if model_name == "twitter_roberta_latest":
                        # Handle labels specifically for the Twitter RoBERTa model
                        if label in ['positive', 'negative', 'neutral']:
                            df.at[i, f'{label.capitalize()}-{model_name}'] = score
                    else:
                        # Handle other models including Amazon Review for star ratings
                        if 'positive' in label:
                            df.at[i, f'Positive-{model_name}'] = score
                        elif 'negative' in label:
                            df.at[i, f'Negative-{model_name}'] = score
                        elif 'neutral' in label:
                            df.at[i, f'Neutral-{model_name}'] = score
                        elif 'star' in label:  # Handle star ratings from Amazon Review model
                            star_rating = label.split()[0]  # Get the star rating (e.g., '5' from '5 stars')
                            df.at[i, f'{star_rating} Star-{model_name}'] = score

        except Exception as e:
            logging.error(f"Error processing RecKey {rec_key}: {e}")

# Display the updated DataFrame
print(df.head())

# Save the updated DataFrame to a new CSV file
output_file_path = r'C:\Users\FourieH\final_transcription_results.csv'
df.to_csv(output_file_path, index=False)


Part 3:
import os
import torch
import torchaudio
import logging
import numpy as np
import pandas as pd
from transformers import Wav2Vec2Processor, Wav2Vec2Model, Wav2Vec2PreTrainedModel, pipeline
from concurrent.futures import ThreadPoolExecutor, as_completed

# Set up logging
logging.basicConfig(level=logging.INFO)

# Constants
RECORDINGS_DIR = r"X:\Bidvest_ASC_recordings"  # Directory where the recordings are stored
SAMPLE_RATE = 16000  # Desired sample rate for processing

# Load the CSV file
csv_file_path = r"C:\Users\FourieH\Desktop\final_transcription_results_diarized.csv"  # Update with your actual CSV file path
data = pd.read_csv(csv_file_path)

# Function to modify the hyperlink with a check for NaN values
def modify_hyperlink(original_hyperlink):
    if isinstance(original_hyperlink, str):
        return original_hyperlink.replace(r"\\172.21.18.180\CallCentreAudioFiles", "X:\\")
    return original_hyperlink  # Return as-is if NaN or not a string

# Apply the modified function to the column
data['Modified Hyperlink'] = data['Segment Audio Hyperlink'].apply(modify_hyperlink)

# Updated function to load and preprocess audio as a NumPy array
def load_and_resample_audio(file_path, sample_rate=SAMPLE_RATE):
    waveform, original_sample_rate = torchaudio.load(file_path)

    # Convert to mono if stereo
    if waveform.size(0) > 1:
        waveform = torch.mean(waveform, dim=0, keepdim=True)

    # Resample if the original sample rate differs from the desired sample rate
    if original_sample_rate != sample_rate:
        resampler = torchaudio.transforms.Resample(orig_freq=original_sample_rate, new_freq=sample_rate)
        waveform = resampler(waveform)

    return waveform.squeeze(0).numpy()

class RegressionHead(torch.nn.Module):
    """Classification head for emotions."""
    def __init__(self, config):
        super().__init__()
        self.dense = torch.nn.Linear(config.hidden_size, config.hidden_size)
        self.dropout = torch.nn.Dropout(config.final_dropout)
        self.out_proj = torch.nn.Linear(config.hidden_size, config.num_labels)

    def forward(self, features):
        x = self.dropout(features)
        x = self.dense(x)
        x = torch.tanh(x)
        x = self.dropout(x)
        return self.out_proj(x)

class EmotionModel(Wav2Vec2PreTrainedModel):
    """Speech emotion classifier."""
    def __init__(self, config):
        super().__init__(config)
        self.config = config
        self.wav2vec2 = Wav2Vec2Model(config)
        self.classifier = RegressionHead(config)
        self.init_weights()

    def forward(self, input_values):
        outputs = self.wav2vec2(input_values)
        hidden_states = outputs[0]
        hidden_states = torch.mean(hidden_states, dim=1)
        logits = self.classifier(hidden_states)
        return logits

# Load the emotion classification model from Hugging Face hub
device = 'cpu'
model_name = 'audeering/wav2vec2-large-robust-12-ft-emotion-msp-dim'
processor = Wav2Vec2Processor.from_pretrained(model_name)
model = EmotionModel.from_pretrained(model_name).to(device)
model.eval()  # Set the model to evaluation mode

# Load the audio classification model
logging.info("Loading audio classification model...")
audio_classifier = pipeline("audio-classification", model="somosnlp-hackathon-2022/wav2vec2-base-finetuned-sentiment-classification-MESD")
logging.info("Audio classification model loaded successfully.")

# Define emotion labels for the emotion model
emotion_labels = ['Arousal', 'Dominance', 'Valence']

# Initialize columns for emotion scores
for label in emotion_labels:
    data[f'Emotion Score - {label}'] = None

# Initialize columns for audio classification scores
data['Happiness'] = None
data['Anger'] = None
data['Neutral'] = None

# Function to predict emotions from raw audio signal
def process_func(x: np.ndarray, sampling_rate: int) -> np.ndarray:
    inputs = processor(x, sampling_rate=sampling_rate, return_tensors="pt")
    input_values = inputs['input_values'].to(device)

    with torch.no_grad():
        logits = model(input_values)

    return logits.detach().cpu().numpy()

# Function to process a single row
def process_row(index, row):
    modified_hyperlink = row['Modified Hyperlink']
    try:
        # Extract filename for logging if modified_hyperlink is valid
        filename = os.path.basename(modified_hyperlink)
        
        if filename.endswith(".wav"):
            preprocessed_audio = load_and_resample_audio(modified_hyperlink)
            
            # Process emotion prediction
            results = process_func(preprocessed_audio, SAMPLE_RATE)
            for label, score in zip(emotion_labels, results[0]):
                data.at[index, f'Emotion Score - {label}'] = score
                logging.info(f"{filename}: {label}: {score:.4f}")

            # Run audio classification
            classification_results = audio_classifier(preprocessed_audio, sample_rate=SAMPLE_RATE)
            for result in classification_results:
                label = result['label']
                score = result['score']
                if label in data.columns:
                    data.at[index, label] = score
                    logging.info(f"{filename} (Audio Classifier): {label}: {score:.4f}")

    except TypeError:
        logging.warning(f"Skipping entry at index {index} due to TypeError.")
    except Exception as e:
        logging.error(f"Error processing {filename}: {e}")

# Use ThreadPoolExecutor for parallel processing
with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
    futures = {executor.submit(process_row, index, row): index for index, row in data.iterrows()}
    for future in as_completed(futures):
        index = futures[future]
        try:
            future.result()
        except Exception as exc:
            logging.error(f"Index {index} generated an exception: {exc}")

# Save the updated DataFrame back to a new CSV file
output_csv_path = r"C:\Users\FourieH\Desktop\final_transcription_results_diarizedv2.csv"   # Update with desired output path
data.to_csv(output_csv_path, index=False)