
from asyncio.windows_events import NULL
from msvcrt import getch
from pickle import NONE
from time import sleep

import ctypes
from ctypes import CFUNCTYPE, c_int64, c_int, c_void_p, c_char_p
import json
import struct
import threading
import wave
import dubbing_sdk_python.dubbing_engine
import dubbing_sdk_python.dubbing_base

setVoiceSuccess = False


# Define our callback function.
@dubbing_sdk_python.dubbing_engine.thread_safe_callback(ctypes.CFUNCTYPE(None, ctypes.c_int64, ctypes.c_int, ctypes.c_int, ctypes.c_int))
def downloadCallback(dubbingEngineId, percent, index, count):
    print("dubbing engine id:", dubbingEngineId, "  download count: ", count, "  index: ", index, "  percent: ", percent)

@dubbing_sdk_python.dubbing_engine.thread_safe_callback(ctypes.CFUNCTYPE(None, ctypes.c_int64))
def completeCallback(dubbingEngineId):
    print("dubbing engine id:", dubbingEngineId, "  download complete")

@dubbing_sdk_python.dubbing_engine.thread_safe_callback(ctypes.CFUNCTYPE(None, ctypes.c_int64))
def errorCallback(dubbingEngineId):
    print("dubbing engine id:", dubbingEngineId, "  download error")

@dubbing_sdk_python.dubbing_engine.thread_safe_callback(ctypes.CFUNCTYPE(None, ctypes.c_int64, ctypes.c_int, ctypes.c_int, ctypes.c_char_p))
def actionResultCallback(dubbingEngineId, actionType, retCode, msg):
    print("dubbing engine id:", dubbingEngineId, "  action type:", actionType, "  ret code:", retCode, "  msg:", msg)
    if actionType == dubbing_sdk_python.dubbing_base.DubbingAction.SET_VOICE:
        if retCode == dubbing_sdk_python.dubbing_base.DubbingRetCode.SUCCESS:
            global setVoiceSuccess
            setVoiceSuccess = True
            print("setVoiceSuccess = True")

def transformWavFile():
    # Open an audio file
    with wave.open('48k.wav', 'rb') as wav_file:
        # Get audio file parameters
        params = wav_file.getparams()
        # Read audio data
        frames  = wav_file.readframes(params.nframes)
        audio_data = bytearray(frames)

    # Define the chunk size for each processing step
    chunk_size = 16 * 480

    token = b"access_key=\"xxxxxx\",timestamp=\"xxxxxx\",nonce=\"xxxxxx\",id=\"xxxxxx\",signature=\"xxxxxx\""

    # Initialize engine data
    print("start initializing engine data, The process may require downloading, please be patient and wait...")
    resPath = b"./dubbing_resource"
    dubbingEngineId = dubbing_sdk_python.dubbing_engine.createDubbingEngineByPool(resPath, token, params.framerate, params.nchannels, dubbing_sdk_python.dubbing_base.AudioSampleFormat.AUDIO_PCM_S16, True, False, True, True, downloadCallback, completeCallback, errorCallback, actionResultCallback)
    if dubbingEngineId == 0 :
        print("create dubbing engine failed")
        return
    result = dubbing_sdk_python.dubbing_engine.prepare(dubbingEngineId)
    if result != dubbing_sdk_python.dubbing_base.DubbingRetCode.SUCCESS :
        print("engine prepare failed")
        return
    status = dubbing_sdk_python.dubbing_engine.getEngineStatus(dubbingEngineId)
    print("getEngineStatus: ", status)
    while status != 2:
        status = dubbing_sdk_python.dubbing_engine.getEngineStatus(dubbingEngineId)
        #print("getEngineStatus: ", status)
        sleep(0.2)
    print("Initialize engine data completed.")
    dubbing_sdk_python.dubbing_engine.start(dubbingEngineId);
    # Get voice list.
    voices = dubbing_sdk_python.dubbing_engine.getVoiceList(dubbingEngineId)
    print(voices)
    jsondata = json.loads(voices)
    if len(jsondata) < 0 :
        print("voices data is null.")
        return
    voiceId = jsondata[0]["id"]
    # Set voice.
    print("set voice begin.")
    result = dubbing_sdk_python.dubbing_engine.setVoice(dubbingEngineId, voiceId)   
    if result != dubbing_sdk_python.dubbing_base.DubbingRetCode.SUCCESS :
        print("set voice failed")
        return
    global setVoiceSuccess
    while setVoiceSuccess == False :
        print("setVoiceSuccess: ", setVoiceSuccess)
        sleep(0.2)
    print("set voice completed.")
    # Start processing.
    with open('out.pcm', 'wb') as pcm_file:
        for i in range(0, len(audio_data), chunk_size):
            chunk = audio_data[i:i+chunk_size]
            byte_arr = (ctypes.c_byte * len(chunk))(*chunk)
            dubbing_sdk_python.dubbing_engine.transform(dubbingEngineId, byte_arr, len(byte_arr))
            # Get the processing result.
            byte_arr = ctypes.cast(byte_arr, ctypes.POINTER(ctypes.c_byte * len(byte_arr))).contents
            pcm_file.write(byte_arr)
    print("destroy dubbing engine")
    dubbing_sdk_python.dubbing_engine.destroyDubbingEngineByPool(dubbingEngineId);
    print("end")

def transformDeviceAudio():
    print("transformDeviceAudio")

def main():
    print("===========help=============")
    print("transformWavFile [defalut]")
    
    # Must be called in the main thread. 
    dubbing_sdk_python.dubbing_engine.initialize_python_threads()
    
    transformWavFile()
    getch()
    getch()
    getch()

main()
