From d2bac8136394d474842e8bf38ec7e14cc0a6494b Mon Sep 17 00:00:00 2001 From: jiyoungcheon Date: Wed, 13 Nov 2024 20:29:26 -0500 Subject: [PATCH] =?UTF-8?q?icsstt=20=EB=AA=A8=EB=93=88=20=EB=B9=A0?= =?UTF-8?q?=EC=A0=B8=EC=9E=88=EB=8D=98=EA=B2=83=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- icsstt/data.go | 180 ++++++++++ icsstt/googlestt.go | 48 +++ icsstt/stt_test.go | 45 +++ icsstt/sttcontroller.go | 449 +++++++++++++++++++++++++ icsstt/sttselvas.go | 727 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1449 insertions(+) create mode 100644 icsstt/data.go create mode 100644 icsstt/googlestt.go create mode 100644 icsstt/stt_test.go create mode 100644 icsstt/sttcontroller.go create mode 100644 icsstt/sttselvas.go diff --git a/icsstt/data.go b/icsstt/data.go new file mode 100644 index 0000000..30ab0c0 --- /dev/null +++ b/icsstt/data.go @@ -0,0 +1,180 @@ +package stt + +import ( + "math" + "sync" + + "mybatch/icslog" +) + +const RR_200MSEC_SIZE = 3200 + +type RRData struct { + wPos int + wTPos int + wtSize int + rPos int + size int // max size + audio []byte + rtAudio []byte //for GetAudio() + rtAudioLen int + m *sync.Mutex +} + +func NewRRData(size int) *RRData { + if size <= 0 { + return nil + } + rr := RRData{wPos: 0, rPos: 0, size: size, rtAudioLen: 0} + rr.m = &sync.Mutex{} + rr.audio = make([]byte, size) + rr.rtAudio = make([]byte, size) + + return &rr +} + +func (rr *RRData) AddAudio(data []byte) int { + l := icslog.InitializeLogger() + + size := len(data) + if size <= 0 || rr.audio == nil { + return -1 + } + + rr.m.Lock() + if rr.wPos == 0 && rr.wTPos != 0 { + rr.wPos = rr.wtSize + } + + if rr.wPos+size > rr.size { + l.Printf("AddAudio> RRData write size exceeded!!! size: %d-%d", rr.size, rr.wPos+size) + rr.wPos = rr.wtSize + //l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "AddAudio> RRData write size exceeded. wPos reset. %d-%d-%d", rr.wPos, rr.size, rr.wPos+size) + } + + //l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "AddAudio> wPos: %d, size: %d, total size: %d", rr.wPos, size, rr.size) + + copy(rr.audio[rr.wPos:], data[:]) + rr.wPos += size + + if rr.size <= rr.rtAudioLen { + rr.rtAudio = append(rr.rtAudio, data...) + } else { + copy(rr.rtAudio[rr.rtAudioLen:], data) + } + rr.rtAudioLen += size + + rr.m.Unlock() + + return rr.wPos +} + +func (rr *RRData) AddTempAudio(data []byte) int { + //1sec temp buffer + if rr.size < 40000 || rr.audio == nil { + return -1 + } + + size := len(data) + if size <= 0 { + return -1 + } + + rr.m.Lock() + + //l := icslog.GetIcsLog() + //l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "AddTempAudio> wtPos: %d, size: %d", rr.wTPos, size) + + if rr.wTPos+size > 40000 { + //rr.wTPos = 0 + rr.wtSize = 40000 + rr.audio = rr.audio[size:] + copy(rr.audio[rr.wTPos-size:], data[:]) + } else { + rr.wtSize += size + copy(rr.audio[rr.wTPos:], data[:]) + rr.wTPos += size + } + + rr.m.Unlock() + + return rr.wTPos +} + +func (rr *RRData) ReadAudio(size int) (int, []byte) { + l := icslog.InitializeLogger() + + if rr.audio == nil { + return -2, nil + } + + rr.m.Lock() + if int(math.Abs(float64(rr.wPos-rr.rPos))) < 1600 || int(math.Abs(float64(rr.wPos-rr.rPos))) < size { + rr.m.Unlock() + return 0, nil + } + rData := make([]byte, size) + + if rr.size < rr.rPos+size { + rr.m.Unlock() + l.Printf("ReadAudio> RRData read size exceeded!!! size: %d-%d", rr.size, rr.rPos+size) + return -1, nil + //rr.rPos = 0 + } + + //l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "ReadAudio> rPos: %d, size: %d, total size: %d", rr.rPos, size, rr.size) + + copy(rData[:], rr.audio[rr.rPos:rr.rPos+size]) + //fmt.Println("read audio data:", rr.rPos) + rr.rPos += size + + rr.m.Unlock() + + return rr.rPos, rData +} + +func (rr *RRData) Clear() { + rr.m.Lock() + rr.wPos = 0 + rr.rPos = 0 + rr.audio = nil + rr.rtAudio = nil + rr.m.Unlock() + + l := icslog.InitializeLogger() + l.Print("Cleared RRData") +} + +func (rr *RRData) GetAudio() []byte { + if rr.audio == nil { + return nil + } + + rr.m.Lock() + + if rr.rtAudioLen <= 0 { + rr.m.Unlock() + return nil + } + //l.Printf(icslog.LOG_LEVEL_DEBUG2, -1, "GetAudio> audio length: %d", rr.rtAudioLen) + + var rData []byte + if rr.wtSize > 0 { + rData = make([]byte, rr.wtSize+rr.rtAudioLen) + copy(rData, rr.audio[:rr.wtSize]) + copy(rData[rr.wtSize:], rr.rtAudio) + } else { + rData = make([]byte, rr.rtAudioLen) + copy(rData, rr.rtAudio) + } + + rr.m.Unlock() + + return rData +} + +func (rr *RRData) Close() { + rr.audio = nil + rr.rtAudio = nil + //rr = new(RRData) +} diff --git a/icsstt/googlestt.go b/icsstt/googlestt.go new file mode 100644 index 0000000..7c7298a --- /dev/null +++ b/icsstt/googlestt.go @@ -0,0 +1,48 @@ +package stt + +import ( + "cloud.google.com/go/speech/apiv1" + "cloud.google.com/go/speech/apiv1/speechpb" + "context" + "log" + "os" +) + +func RecognizeSpeech(language, filepath string) error { + ctx := context.Background() + client, err := speech.NewClient(ctx) + if err != nil { + return err + } + defer client.Close() + + audioData, err := os.ReadFile(filepath) + if err != nil { + return err + } + + req := &speechpb.RecognizeRequest{ + Config: &speechpb.RecognitionConfig{ + Encoding: speechpb.RecognitionConfig_LINEAR16, + SampleRateHertz: 16000, + LanguageCode: language, + }, + Audio: &speechpb.RecognitionAudio{ + AudioSource: &speechpb.RecognitionAudio_Content{ + Content: audioData, + }, + }, + } + + resp, err := client.Recognize(ctx, req) + if err != nil { + return err + } + + for _, result := range resp.Results { + for _, alt := range result.Alternatives { + log.Printf("Transcript: %s\n", alt.Transcript) + } + } + return nil +} diff --git a/icsstt/stt_test.go b/icsstt/stt_test.go new file mode 100644 index 0000000..cd320bb --- /dev/null +++ b/icsstt/stt_test.go @@ -0,0 +1,45 @@ +package stt + +import ( + "fmt" + "mybatch/icsconf" + "mybatch/icserror" + "os" + "strconv" + "testing" +) + +var config icsconf.AppInfo + +func init() { + config = icsconf.Getconfig() +} + +func TestXXX(t *testing.T) { + data, _ := os.ReadFile("/home/prac/svc/icsbc/voice/pcm/20250913223412/20250913223412-left.pcm") + connectSttServer() + sttResTest(data) +} + +func connectSttServer(voicedata []byte) (bool, *icserror.IcsError) { + var s *STTSelvas + var sttErr *icserror.IcsError + ip := config.STT.SrcIP + port, _ := strconv.Atoi(cfg.STT.Port) + callId := "00ef1234567899001" + custid := "3002" + RecordFilePath := "/home/prac/svc/icsbc/sttres" + + // func NewSTTS(sid int, IP string, port int, callID string, custID string, filePath string) + s, sttErr = NewSTTS(ip, port, callId, custid, RecordFilePath) + if sttErr != nil { + return false, icserror.ICSERRSTTConnectFail + } else if s != nil { + fmt.Println("STT session:", s) + } + fmt.Println("stt server is connected with: ", ip, ", port: ", port) + return true, nil +} +func sttResTest(voicedata []byte) (bool, *icserror.IcsError) { + +} diff --git a/icsstt/sttcontroller.go b/icsstt/sttcontroller.go new file mode 100644 index 0000000..4959da6 --- /dev/null +++ b/icsstt/sttcontroller.go @@ -0,0 +1,449 @@ +package stt + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "mybatch/icsconf" + encry "mybatch/icsencry" + "mybatch/icserror" + httprequest "mybatch/icshttp" + "mybatch/icslog" + + "github.com/gorilla/websocket" +) + +type RequestData struct { + StartTime string `json:"starttime"` + Ext string `json:"ext"` + ConnID string `json:"connid"` +} + +type FileListResponse struct { + TotalCnt string `json:"totalCnt"` + ReturnCode string `json:"returnCode"` + ReturnMsg string `json:"returnMsg"` + List []FileInfo `json:"list"` +} + +type FileInfo struct { + No int `json:"no"` + ConnID string `json:"connid"` + Ext string `json:"ext"` + StartTime string `json:"starttime"` +} + +type Transcript struct { + Text string `json:"text"` + StartTime int `json:"startTime"` + EndTime int `json:"endTime"` + EndPoint bool `json:"endPoint"` +} + +type ResponseData struct { + Uid string `json:"uid"` + Ext string `json:"ext"` + SpeakerTag string `json:"speakerTag"` + Transcripts []Transcript `json:"transcripts"` +} + +type TAMessage struct { + Cmd string `json:"cmd"` // STT 이벤트 상태 구분값 S:시작, E:종료, T:Text, P:Partial + ConnID string `json:"connid"` //Cti 생성콜 key + Tel string `json:"tel"` //고객 전화번호 + Ext string `json:"ext"` //상담사 내선번호 + CallStartTime string `json:"callStartTime"` //콜 시작시간 unixtime + IgGbn string `json:"igGbn"` //통화유형 0:알수없음, 1:인바운드, 2:아웃바운드 + DateTime string `json:"datetime"` //STT 시작시간 + Stt string `json:"stt"` //STT 결과 + Dir string `json:"dir"` //STT 화자구분 R:고객, T:상담사 +} + +var ( + cfg icsconf.AppInfo + l *log.Logger +) + +func init() { + cfg = icsconf.Getconfig() + l = icslog.InitializeLogger() +} + +func ProcessPostRequestForDataList() { + var starttime uint64 + currentDate := time.Now().Format("20060102") + currentDateUint64, _ := strconv.ParseUint(currentDate, 10, 64) + + starttime = currentDateUint64 - 1 + starttimeStr := strconv.FormatUint(starttime, 10) + + data := RequestData{ + StartTime: starttimeStr, + Ext: "", + ConnID: "", + } + + jsonData, _ := json.Marshal(data) + encryptedRequestBody := encry.Encrypting(jsonData) + + url := "https://192.168.0.69:8080/sttRest.do" + method := "POST" + requestBody := fmt.Sprintf(`{"data":"%s"}`, encryptedRequestBody) + + response, err := httprequest.HttpRequest(url, method, requestBody) + if err != nil { + l.Println("error occured while requesting http post for datalist:", err) + return + } + + defer response.Body.Close() + body, _ := io.ReadAll(response.Body) + + fmt.Println("응답 on ProcessPostRequestForDataList: ", string(body)) + + var parsedRes FileListResponse + if err := json.Unmarshal([]byte(body), &parsedRes); err != nil { + l.Println("Error parsing JSON:", err) + return + } + + var reqDataForVoiceFile RequestData + if len(parsedRes.List) > 0 { + for _, item := range parsedRes.List { + reqDataForVoiceFile = RequestData{ + StartTime: item.StartTime, + Ext: item.Ext, + ConnID: item.ConnID, + } + // Requesting For wav file and download it at local + postReqForWavFileAndDownload(reqDataForVoiceFile) + } + } else { + l.Println("No items in the datalist.") + } +} + +func postReqForWavFileAndDownload(parsedRes RequestData) { + jsonData, _ := json.Marshal(parsedRes) + encryptedRequestBody := encry.Encrypting(jsonData) + + url := "https://192.168.0.69:8080/sttRest.do" + method := "POST" + requestBody := fmt.Sprintf(`{"data":"%s"}`, encryptedRequestBody) + + response, err := httprequest.HttpRequest(url, method, requestBody) + if err != nil { + l.Println("error occured while requesting http post for datalist:", err) + return + } + st := parsedRes.StartTime + wavFilePath := cfg.Directories.WAVDirectory + fileName := fmt.Sprintf(`%s/%s.wav`, wavFilePath, st) + file, err := os.Create(fileName) + if err != nil { + l.Println("error occured while creating file:", err) + return + } + defer file.Close() + + _, err = io.Copy(file, response.Body) + if err != nil { + l.Println("error occured while writing to file:", err) + return + } + + // 잘 쪼개졌다면 각 left, right을 stt 돌리기 + pcmResult, folderName := devideWavTo2Pcm(fileName) + + if pcmResult { + sttCallRes, err := controlSTT(parsedRes, folderName) + if err != nil { + l.Println("runSTT() failed with the error: ", err) + } + // stt 콜이 성공이라면 pcm 파일 지우기 + if sttCallRes { + pcmDir := filepath.Join(cfg.Directories.PCMDirectory, st) + deletePcmFolder(pcmDir) + + } + } +} + +func deletePcmFolder(foldername string) (bool, *icserror.IcsError) { + fmt.Println("foldername in deletePcmFolder func: ", foldername) + if _, err := os.Stat(foldername); os.IsNotExist(err) { + l.Println("pcm folder does not exist with the dir: ", foldername) //error here + l.Println("error: ", err) + return false, icserror.ICSERRFileOpen + } + err := os.RemoveAll(foldername) + if err != nil { + l.Panicln("error occured while deleting folder: ", foldername) + return false, icserror.ICSERRWriteFile + } + l.Printf(`folder: %s has been successfully deleted`, foldername) + return true, nil +} + +func devideWavTo2Pcm(fileName string) (result bool, folderName string) { + // fileName: /home/prac/svc/icsbc/voice/wav/20250913223412.wav + fileNameWithoutWav := fileName[:len(fileName)-4] + lastSlashIdx := strings.LastIndex(fileNameWithoutWav, "/") + starttime := fileNameWithoutWav[lastSlashIdx+1:] //starttime: 20250913223412 + pcmDir := cfg.Directories.PCMDirectory + + starttimeDir := fmt.Sprintf("%s/%s", pcmDir, starttime) + os.MkdirAll(starttimeDir, os.ModePerm) + + leftOutputFile := fmt.Sprintf(`%s/%s-left.pcm`, starttimeDir, starttime) + rightOutputFile := fmt.Sprintf(`%s/%s-right.pcm`, starttimeDir, starttime) + fmt.Println("inFile reading wav path: ", fileName) + + inFile, err := os.Open(fileName) + if err != nil { + l.Println("failed to open input file: %v", err) + return false, "" + } + defer inFile.Close() + + leftFile, err := os.Create(leftOutputFile) + if err != nil { + l.Println("failed to create left channel file: %v", err) + return false, "" + } + defer leftFile.Close() + + rightFile, err := os.Create(rightOutputFile) + if err != nil { + l.Println("failed to create right channel file: %v", err) + return false, "" + } + defer rightFile.Close() + + // Skip WAV header (44 bytes) + wavHeader := make([]byte, 44) + if _, err := inFile.Read(wavHeader); err != nil { + l.Println("failed to read WAV header: %v", err) + return false, "" + } + + // Check if it's a valid WAV file + if string(wavHeader[:4]) != "RIFF" || string(wavHeader[8:12]) != "WAVE" { + l.Println("invalid WAV file format") + return false, "" + } + + // Check if WAV file is stereo + numChannels := binary.LittleEndian.Uint16(wavHeader[22:24]) + if numChannels != 2 { + l.Println("unsupported channel count: this function only supports stereo (2 channels)") + return false, "" + } + + buf := make([]byte, 4) // 2 bytes per sample * 2 channels + + for { + n, err := inFile.Read(buf) + if err == io.EOF { + break + } + if err != nil { + l.Println("failed to read from input file: %v", err) + return false, "" + } + + if n < 4 { + l.Printf("unexpected sample size read: %d\n", n) + // Break or handle the case based on your needs + for i := n; i < 4; i++ { + buf[i] = 0 + } + break + } + + leftSample := buf[:2] + rightSample := buf[2:] + + if err := binary.Write(leftFile, binary.LittleEndian, leftSample); err != nil { + l.Println("failed to write to left channel file: %v", err) + return false, "" + } + + if err := binary.Write(rightFile, binary.LittleEndian, rightSample); err != nil { + l.Println("failed to write to right channel file: %v", err) + return false, "" + } + + } + + l.Println("WAV file split successfully") + return true, starttime +} + +// server 연결, stt 호출, 연결 해제 +func controlSTT(parsedRes RequestData, folderName string) (bool, *icserror.IcsError) { + pcmDirectory := cfg.Directories.PCMDirectory + pcmDirectory = filepath.Join(pcmDirectory, folderName) + + // walking the file that has each left and right pcm files + filepath.Walk(pcmDirectory, func(path string, info os.FileInfo, err error) error { + if err != nil { + l.Println("Error occured while walking pcm folder. error: ", err) + l.Println("The pcm file path: ", pcmDirectory) + return err + } + if !info.IsDir() && filepath.Ext(path) == "left.pcm" { + stterr := connectSelvasServerRunSTT(parsedRes, path, "left") + if stterr != nil { + l.Println("Error occured on recognizeSpeechFromPcmFile(). error: ", stterr) + return err + } + } + if !info.IsDir() && filepath.Ext(path) == "right.pcm" { + stterr := connectSelvasServerRunSTT(parsedRes, path, "right") + if stterr != nil { + l.Println("Error occured on recognizeSpeechFromPcmFile(). error: ", stterr) + return err + } + } + return nil + }) + return true, nil +} + +func connectSelvasServerRunSTT(parsedRes RequestData, filePath string, leftright string) *icserror.IcsError { + var sttRes NewSTTResult + var sttErr *icserror.IcsError + + voicedata, err := os.ReadFile(filePath) + if err != nil { + return icserror.ICSERRCONFOpenFile + } + ip := cfg.STT.SrcIP + port, _ := strconv.Atoi(cfg.STT.Port) + callId := parsedRes.ConnID + custId := parsedRes.Ext + recordFilePath := cfg.Directories.RecDirectory + + // connecting with STT server + s, sttErr := NewSTTS(ip, port, callId, custId, recordFilePath) + if sttErr != nil { + return icserror.ICSERRSTTConnectFail + } else if s != nil { + fmt.Println("STT session:", s) + } + l.Println("STT server is connected with: ", ip, ", port: ", port) + + // STT 호출 + sttRes, sttErr = s.SendSTT(voicedata, true, nil) + if sttErr != nil { + l.Println("calling sendSTT() on sttselvas.go has failed with error: ", sttErr) + return icserror.ICSERRSTTSendFail + } + + // STT server and chanel close + s.CloseChanelAndServer() + + // STT 호출 성공시 결과를 websocket, ta rest로 송신 + if sttRes.Res != "" { + wssres, wsserr := webSocketSend(parsedRes, sttRes, leftright) + httpres, httperr := taRestSend(parsedRes, sttRes, s.GetReqTime(), leftright) + if wssres && httpres { + l.Println("stt results have successfully sent through both wss and http(ta)") + } + if wsserr != nil { + l.Println("sending stt result through websocket has failed with the error: ", wsserr) + } + if httperr != nil { + l.Println("sending stt result through http for ta has failed with the error: ", httperr) + } + } + return nil +} + +func webSocketSend(parsedRes RequestData, sttRes NewSTTResult, leftright string) (bool, *icserror.IcsError) { + speaker := "X" // T: 상담사 + if leftright == "left" { + speaker = "R" //R: 고객 + } + url := "ws://192.168.0.69:8090/wss" + headers := http.Header{ + "Origin": {"https://192.168.0.69"}, + } + conn, _, err := websocket.DefaultDialer.Dial(url, headers) + if err != nil { + l.Println("Failed to connect to WebSocket: %v", err) + return false, icserror.ICSERRWEBSOCKETConnectFailError + } + defer conn.Close() + + resData := ResponseData{ + Uid: parsedRes.ConnID, + Ext: parsedRes.Ext, + SpeakerTag: speaker, + Transcripts: []Transcript{ + { + Text: sttRes.Res, + StartTime: sttRes.NStart, + EndTime: sttRes.NEnd, + EndPoint: true, + }, + }, + } + + jsonData, _ := json.Marshal(resData) + encryptedResponseBody := encry.Encrypting(jsonData) + + responseBody := fmt.Sprintf(`{"data":"%s"}`, encryptedResponseBody) + + err = conn.WriteMessage(websocket.TextMessage, []byte(responseBody)) + if err != nil { + l.Println("failed to send msg via websocket with the err: ", err) + return false, icserror.ICSERRWEBSOCKETWriteError + } else { + l.Println("Sent the msg via websocket. message body: ", responseBody) + return true, nil + } +} + +func taRestSend(parsedRes RequestData, sttRes NewSTTResult, reqTime time.Time, leftright string) (bool, *icserror.IcsError) { + dir := "T" // T: 상담사 + if leftright == "left" { + dir = "R" //R: 고객 + } + resData := TAMessage{ + Cmd: "T", + ConnID: parsedRes.ConnID, + Tel: "", // 정보없음 + Ext: parsedRes.Ext, + CallStartTime: parsedRes.StartTime, + IgGbn: "1", // 인바운드 + DateTime: reqTime.Format("2006-01-02 15:04:05"), + Stt: sttRes.Res, + Dir: dir, + } + jsonData, _ := json.Marshal(resData) + encryptedRequestBody := encry.Encrypting(jsonData) + + url := "https://192.168.0.69:8080/taRest.do" + method := "POST" + requestBody := fmt.Sprintf(`{"data":"%s"}`, encryptedRequestBody) + + response, err := httprequest.HttpRequest(url, method, requestBody) + if err != nil { + l.Println("error occured while requesting http post for datalist:", err) + return false, icserror.ICSERRHTTPClientExcecution + } + + l.Println("TA msg has been successfully sent. response: ", response) + return true, nil +} diff --git a/icsstt/sttselvas.go b/icsstt/sttselvas.go new file mode 100644 index 0000000..22d7f44 --- /dev/null +++ b/icsstt/sttselvas.go @@ -0,0 +1,727 @@ +//go:build !test +// +build !test + +// for STT SELVAS STT +package stt + +/* +#cgo LDFLAGS: -lstdc++ -ldl ./extlib/selvasstt/SDK/LIB/c_linux/x64/libASRLIB.a ./extlib/selvasstt/SDK/OpenSSL/Linux/Linux_x64/libssl.a ./extlib/selvasstt/SDK/OpenSSL/Linux/Linux_x64/libcrypto.a +#cgo CFLAGS: -I ../extlib/selvasstt/SDK/INCLUDE +#include +#include +#include +#include + +char* getResultData(LVCSR_DATA_RESULT* pDataResult, long long nCount) { + char* skip1 = ""; + char* space = " "; + int len = 0; + long long i; + + for (i = 0; i < nCount; i++) { + len += strlen(pDataResult[i].pTokenStr) + 1; + printf("start: %ld, end: %ld\n", pDataResult[i].nStart, pDataResult[i].nEnd); + } + + char* result = malloc(sizeof(char) * len); + strcpy(result, ""); + + for (i = 0; i < nCount; i++) { + if (strcmp(pDataResult[i].pTokenStr,skip1) == 0) { + } else { + strcat(result, space); + strcat(result, pDataResult[i].pTokenStr); + } + } + + return result; +} + +void freeResult(char* result){ + if(result == NULL) { + return; + } + free(result); +} + +int getMidResultEPD(LVCSR_RECOG_MID_RESULT *midResult){ + return midResult->bEngineDetectionFlag; +} + +void printMidResult(LVCSR_RECOG_MID_RESULT *midResult){ + printf("result len : %ld\n", midResult->nResultLen); + printf("pResult : %s\n", midResult->pResult); + printf("flag : %d\n", midResult->bEngineDetectionFlag); + printf("count: %ld\n\n", midResult->nCount); +} + +void setSid(LVCSR_DATA_LOGINFO *logInfo, char* sid, int size) { + // memset(logInfo.tloItemSId, 0, 51); + // memcpy(logInfo.tloItemSId, sid, size); + sprintf(logInfo->tloItemSId, "%s", sid); + // printf("logInfo.tloItemSId-%s\n ", logInfo.tloItemSId); +} + +void setCallId(LVCSR_DATA_LOGINFO *logInfo, char* callId, int size) { + // memset(logInfo.tloItemCallId, 0, 61); + // memcpy(logInfo.tloItemCallId, callId, size); + sprintf(logInfo->tloItemCallId, "%s", callId); + // printf("logInfo.tloItemCallId-%s\n ", logInfo.tloItemSId); +} + +void setTrId(LVCSR_DATA_LOGINFO *logInfo, char* trId, int size) { + // memset(logInfo.tloItemTransactionId, 0, 101); + // memcpy(logInfo.tloItemTransactionId, trId, size); + sprintf(logInfo->tloItemTransactionId, "%s", trId); + // printf("logInfo.tloItemTransactionId-%s\n ", logInfo.tloItemTransactionId); +} + +void setItemStartMessage(LVCSR_DATA_LOGINFO *logInfo, char* tloTime, int size) { + // memset(logInfo.tloItemStartMessage, 0, 101); + // memcpy(logInfo.tloItemStartMessage, trId, size); + sprintf(logInfo->tloItemStartMessage, "%s", tloTime); + // printf("logInfo.tloItemStartMessage-%s\n ", logInfo.tloItemStartMessage); +} + +long getSTTSockID(LVCSR_SOCK_HEAD sockhead) { + return sockhead.uSock; +} + +*/ +import "C" +import ( + "fmt" + "mybatch/icserror" + "mybatch/icslog" + "runtime/debug" + "strings" + "sync" + "time" + "unsafe" + + // "git.icomsys.co.kr/icomsys/voicegateway/voiceagent/icsconf" + + "github.com/google/uuid" +) + +const ( + CONNECT_TIMEOUT = 3 + READ_CONNECT_TIMEOUT = 5 + READ_CONNECT_TIMEOUT_LONGVOICE = 10 + + // model Info + MODEL_ID = 0 + KWD_ID = -1 + CODEC_TYPE = 0 // 8k + LANGUAGE = 1 // utf-8 + USED_EPD = 1 // epd used + NO_USED_EPD = 0 // epd used + //USED_EPD = 1 // epd used + USED_SCORE = 0 // used off +) + +type STTSelvas struct { + sessionID int + //handle int + authCode string + //ch int + sttID C.long + text string + voiceBuf []byte + voiceBufCur int64 + silencenum int + validnum int64 //rms counter + uDataSize int + uEndOfSpeech int + STTInfo STTInfo + IsClose bool + SpeechStatus int + RecordFilePath string + RecordStart *bool + rrData *RRData + M *sync.Mutex + txNum int //number of sending stt audio packet + callID string + custID string + sreserved string + trid string + errCode string + errFunName string + errMsg string + result string + reqTime time.Time + rspTime time.Time + svcReqTime time.Time + svcRspTime time.Time + sttcount int + sttStatus int + //icsstat.StatInfos + language string +} + +type STTInfo struct { + LVCSR_SOCK_HEAD C.LVCSR_SOCK_HEAD + LVCSR_EPD_INFO C.LVCSR_EPD_INFO + LVCSR_DATA_AUTHENTICATION C.LVCSR_DATA_AUTHENTICATION + LVCSR_RECOG_RESULT C.LVCSR_RECOG_RESULT + LVCSR_DATA_RESULT C.LVCSR_DATA_RESULT + LVCSR_RECOG_MID_RESULT C.LVCSR_RECOG_MID_RESULT + LVCSR_DATA_INFO C.LVCSR_DATA_INFO + LVCSR_DATA_LOGINFO C.LVCSR_DATA_LOGINFO + LVCSR_ERROR_RESULT C.LVCSR_ERROR_RESULT +} + +type STTSResult struct { + result string + error *icserror.IcsError +} +type NewSTTResult struct { + Res string + NStart int + NEnd int +} + +func (s STTSelvas) GetTrID() string { + return s.trid +} + +// connect SELVAS STT Server +func NewSTTS(IP string, port int, callID string, custID string, filePath string) (*STTSelvas, *icserror.IcsError) { + //func NewSTTS(sid int, IP string, port int, callID string, custID string, filePath string, sreserved string, statOK bool) (*STTSelvas, *icserror.IcsError) { + l := icslog.InitializeLogger() + var derr *icserror.IcsError = nil + + defer func() { + if err := recover(); err != nil { + switch v := err.(type) { + case error: + icserror.ICSERRSTTFailInit.SetError(v) + l.Printf("PANIC! %s\n%s", icserror.ICSERRSTTFailInit.GetError().Error(), string(debug.Stack())) + default: + l.Print(icserror.ICSERRSTTFailInit.GetError().Error()) + } + } + derr = icserror.ICSERRSTTFailInit + }() + + if len(IP) <= 0 || port <= 0 { + derr = icserror.ICSERRInvalidParam + return nil, derr + } + + stts := STTSelvas{authCode: "LGUPlusManager", uDataSize: 1600, uEndOfSpeech: 0, IsClose: false, SpeechStatus: 0, reqTime: time.Now(), svcReqTime: time.Time{}, svcRspTime: time.Time{}, callID: callID, custID: custID, result: "", errCode: "", errMsg: "", errFunName: ""} + //stts := STTSelvas{handle: -1, authCode: "LGUPlusManager", uDataSize: 1600, uEndOfSpeech: 0, IsClose: false} + + stts.M = &sync.Mutex{} + stts.txNum = 0 + + //recording + stts.RecordFilePath = filePath + stts.RecordStart = new(bool) + *stts.RecordStart = false + stts.rrData = NewRRData(960000) //60sec + + csIP := C.CString(IP) + defer C.free(unsafe.Pointer(csIP)) + csPort := C.long(port) + csConTimeout := C.long(CONNECT_TIMEOUT) + csReadTimeout := C.long(READ_CONNECT_TIMEOUT) + + // 셀바스 API는 현재 통합통계가 있어야 동작을함 + trID := uuid.New().String() + trID = strings.ReplaceAll(trID, "-", "a") + stts.trid = trID + + /* + if stts.sttStatus == STTMemo { + return &stts, nil + } + */ + + //svcReqTime := time.Now() + l.Printf("STT ASR_SVC_OPEN Start") + rc := C.ASR_SVC_OPEN(csIP, csPort, csConTimeout, csReadTimeout, &stts.STTInfo.LVCSR_SOCK_HEAD) // add LVCSR_SOCK_HEAD + if int(rc) == -1 { + ////////////////테스트코드///////////////////// + rc = C.ASR_SVC_GET_ERROR(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_ERROR_RESULT) + if int(rc) < 0 { + l.Printf("[%d] ASR_SVC_GET_ERROR Fail.. %d", int(stts.sttID), rc) + // return "", derr + } else { + l.Printf("[%d] LVCSR_ERROR_RESULT - [%d]%s", int(stts.sttID), stts.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&stts.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + } + l.Printf("STT ASR_SVC_OPEN Fail - rc:%d", rc) + derr = icserror.ICSERRSTTConnectTimeout + return nil, derr + } else if int(rc) == -2 { + l.Printf("STT ASR_SVC_OPEN Fail - rc:%d", rc) + ////////////////테스트코드///////////////////// + rc = C.ASR_SVC_GET_ERROR(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_ERROR_RESULT) + if int(rc) < 0 { + l.Printf("[%d] ASR_SVC_GET_ERROR Fail.. %d", int(stts.sttID), rc) + // return "", derr + } else { + l.Printf("[%d] LVCSR_ERROR_RESULT - [%d]%s ", int(stts.sttID), stts.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&stts.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + } + /////////////////////////////////////////////// + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + // 통합통계 + now := time.Now() + tloTime := fmt.Sprintf("%d%02d%02d%02d%02d%02d", now.Year(), int(now.Month()), now.Day(), now.Hour(), now.Minute(), now.Minute()) + csTrId := C.CString(trID) + defer C.free(unsafe.Pointer(csTrId)) + csCallId := C.CString(callID) + defer C.free(unsafe.Pointer(csCallId)) + csTloTime := C.CString(tloTime) + defer C.free(unsafe.Pointer(csTloTime)) + csCustID := C.CString(custID) + defer C.free(unsafe.Pointer(csCustID)) + + C.setSid(&stts.STTInfo.LVCSR_DATA_LOGINFO, csCustID, C.int(len(custID))) + C.setCallId(&stts.STTInfo.LVCSR_DATA_LOGINFO, csCallId, C.int(len(callID))) + C.setTrId(&stts.STTInfo.LVCSR_DATA_LOGINFO, csTrId, C.int(len(trID))) + C.setItemStartMessage(&stts.STTInfo.LVCSR_DATA_LOGINFO, csTloTime, C.int(len(tloTime))) + + l.Printf("[%d] SET ASR_SVC_SET_LOGINFO > trID:%s, callID:%s, tloTime:%+v, custID:%s", int(stts.sttID), trID, callID, tloTime, custID) + //svcReqTime = time.Now() + rs := C.ASR_SVC_SET_LOGINFO(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_DATA_LOGINFO) // 통합통계 설정 + //ljhwan + //rs = 0 + if int(rs) < 0 { + l.Printf("[%d] STT ASR_SVC_SET_LOGINFO Fail Result rs:%d, LVCSR_DATA_LOGINFO: %+v", int(stts.sttID), rs, stts.STTInfo.LVCSR_DATA_LOGINFO) + ////////////////테스트코드///////////////////// + rc = C.ASR_SVC_GET_ERROR(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_ERROR_RESULT) + if int(rc) < 0 { + l.Printf("[%d] ASR_SVC_GET_ERROR Fail..", int(stts.sttID)) + // return "", derr + } else { + l.Printf("[%d] LVCSR_ERROR_RESULT - [%d]%s", int(stts.sttID), stts.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&stts.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + } + /////////////////////////////////////////////// + + // Server Close + //svcReqTime = time.Now() + l.Printf("[%d] STT ASR_SVC_CLOS Start", int(stts.sttID)) + rc = C.ASR_SVC_CLOS(&stts.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Printf("[%d] STT ASR_SVC_CLOS Fail Result rs:%d", int(stts.sttID), rs) + + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + // Channel Connect + //svcReqTime = time.Now() + l.Printf("[%d] STT ASR_SVC_RECG_OPEN Start", int(stts.sttID)) + rc = C.ASR_SVC_RECG_OPEN(&stts.STTInfo.LVCSR_SOCK_HEAD) // add LVCSR_SOCK_HEAD + //ljhwan + //rc = 0 + if int(rc) < 0 { + l.Printf("[%d] STT ASR_SVC_RECG_OPEN Fail, LVCSR_SOCK_HEAD: %+v\r\n", int(stts.sttID), stts.STTInfo.LVCSR_SOCK_HEAD) + // Server Close + //svcReqTime = time.Now() + l.Printf("[%d] STT ASR_SVC_CLOS Start", int(stts.sttID)) + rc = C.ASR_SVC_CLOS(&stts.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Printf("[%d]STT ASR_SVC_CLOS Fail Result rs:%d, LVCSR_SOCK_HEAD: %+v", int(stts.sttID), rs, stts.STTInfo.LVCSR_SOCK_HEAD) + + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + // Set Model List + stts.STTInfo.LVCSR_DATA_INFO.nModelId = MODEL_ID + stts.STTInfo.LVCSR_DATA_INFO.nKwdId = KWD_ID + stts.STTInfo.LVCSR_DATA_INFO.nCodecType = CODEC_TYPE + stts.STTInfo.LVCSR_DATA_INFO.nCharSet = LANGUAGE + // if stts.sttStatus == STTMemo { + // stts.STTInfo.LVCSR_DATA_INFO.bEpdUsed = NO_USED_EPD + // } else { + // stts.STTInfo.LVCSR_DATA_INFO.bEpdUsed = USED_EPD + // } + stts.STTInfo.LVCSR_DATA_INFO.bScoreUsed = USED_SCORE + + //svcReqTime = time.Now() + rc = C.ASR_SVC_RECG_SET_LIST(&stts.STTInfo.LVCSR_SOCK_HEAD, &stts.STTInfo.LVCSR_DATA_INFO) + //ljhwan + //rc = 0 + if int(rc) < 0 { + l.Printf("[%d]STT ASR_SVC_RECG_SET_LIST Fail Result rs:%d, LVCSR_SOCK_HEAD: %+v, stts.STTInfo.LVCSR_DATA_INFO: %+v", int(stts.sttID), rs, stts.STTInfo.LVCSR_SOCK_HEAD, stts.STTInfo.LVCSR_DATA_INFO) + + // Channel Connection Close + //svcReqTime = time.Now() + l.Printf("[%d]STT ASR_SVC_RECG_CLOS Start", int(stts.sttID)) + rc = C.ASR_SVC_RECG_CLOS(&stts.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Printf("[%d]STT ASR_SVC_RECG_CLOS Fail Result rs:%d, LVCSR_SOCK_HEAD: %+v", int(stts.sttID), rs, stts.STTInfo.LVCSR_SOCK_HEAD) + derr = icserror.ICSERRTTSNotInit + } + + // Server Close + l.Printf("[%d]STT ASR_SVC_CLOS Start", int(stts.sttID)) + //svcReqTime = time.Now() + rc = C.ASR_SVC_CLOS(&stts.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Printf("[%d]STT ASR_SVC_CLOS Fail Result rs:%d, LVCSR_SOCK_HEAD: %+v", int(stts.sttID), rs, stts.STTInfo.LVCSR_SOCK_HEAD) + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + derr = icserror.ICSERRSTTFailInit + return nil, derr + } + + l.Printf("[%d]Selvas STT New Connection > callID:'%s',trID:'%s'", int(stts.sttID), callID, trID) + // fmt.Println("STT: Selvas STT New Connection") + + return &stts, derr +} + +func (s *STTSelvas) Close(save bool) (string, string, *icserror.IcsError) { + if s == nil { + return "", "", icserror.ICSERRSTTNotInit + } + + l := icslog.InitializeLogger() + // conf := icsconf.GetIcsConfig() + var derr *icserror.IcsError = nil + + defer func() { + if err := recover(); err != nil { + switch v := err.(type) { + case error: + icserror.ICSERRSTTFailInit.SetError(v) + l.Printf("PANIC! %s\n%s", icserror.ICSERRSTTFailInit.GetError().Error(), string(debug.Stack())) + default: + l.Print(icserror.ICSERRSTTFailInit.GetError().Error()) + } + derr = icserror.ICSERRSTTFailInit + return + } + // derr = icserror.ICSERRSTTFailInit + }() + + //l.Printf(icslog.LOG_LEVEL_WARN, s.sessionID, "[%d]STT CLOSE : %s", int(s.sttID), string(debug.Stack())) + l.Printf("Close STT[%d]. Send Audio Packet Num: %d", int(s.sttID), s.txNum) + /* + if s.handle < 0 || s.ch < 0 { + fmt.Println("selvas stt", s.ch) + return icserror.ICSERRSTTNotInit + } + */ + + s.M.Lock() + s.IsClose = true + s.M.Unlock() + + var localPath, URL string + + if *s.RecordStart { + if save { + // aud := s.rrData.GetAudio() + // audLen := len(aud) + + // 여기 수정해야함 + + // path, ymd, serr := icsutil.Save(aud, s.RecordFilePath, conf.AppInfo.VoiceConfig.Path, true) + // //path, ymd := Save(s.rrData.GetAudio(), s.RecordFilePath, conf.VoiceConfig.Path, true) + // if serr == nil { + // if len(path) == 0 { + // l.Printf(icslog.LOG_LEVEL_WARN, s.sessionID, "[%d]Failed to save STT voice : %s", int(s.sttID), s.RecordFilePath) + // } else { + // localPath = path + // URL = fmt.Sprintf("%s%s/%s.wav", conf.AppInfo.VoiceConfig.HTTPPath, ymd, s.RecordFilePath) + // s.RecordFilePath = "" + // l.Printf(icslog.LOG_LEVEL_INFO, s.sessionID, "[%d]Saved STT voice(%d). path: %s, URL: %s", int(s.sttID), audLen, localPath, URL) + // } + // } else { + // l.Printf(icslog.LOG_LEVEL_WARN, s.sessionID, "[%d]Failed to save STT voice(%d) : %s, %s - %s", int(s.sttID), audLen, s.RecordFilePath, conf.AppInfo.VoiceConfig.Path, serr.GetMessage()) + // } + } + } + *s.RecordStart = false + + // l.Printf(icslog.LOG_LEVEL_DEBUG2, s.sessionID, "[%d]STT ASR_SVC_RECG_CLOS. sock header: %+v", int(s.sttID), s.STTInfo.LVCSR_SOCK_HEAD.uSock) + + //l.Printf(icslog.LOG_LEVEL_INFO, s.sessionID, "[%d] s.STTInfo.LVCSR_RECOG_RESULT Start - s.STTInfo.LVCSR_RECOG_RESULT:%+v", int(s.sttID), s.STTInfo.LVCSR_RECOG_RESULT) + rc := C.ASR_SVC_RECG_PROC_FREE(&s.STTInfo.LVCSR_RECOG_RESULT) + if int(rc) < 0 { + l.Printf("Failed to free STT result[%d]. rc: %d", int(s.sttID), rc) + if strings.Compare(s.errCode, "54000013") != 0 { + s.errCode, s.errMsg, s.errFunName = "54000010", "Fail", "ASR_SVC_RECG_PROC_FREE" + } + derr = icserror.ICSERRSTTFreeError + // s.SendStasticInfo(s.callID, s.custID, s.trid, "STT", "ASR_SVC_RECG_PROC_FREE", "54000001", "Fail", "", "", s.reqTime, time.Now(), s.sreserved, svcReqTime, time.Now()) + } + + // Channel Connection Close + l.Printf("Close STT channel[%d]", int(s.sttID)) + rc = C.ASR_SVC_RECG_CLOS(&s.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Printf("Failed to close STT channel[%d], rc: %d", int(s.sttID), rc) + if s.result == "$NO_RESULT$" { + s.errCode, s.errMsg, s.errFunName = "20000003", "Success", "" + } else if strings.Compare(s.errCode, "54000013") != 0 { + s.errCode, s.errMsg, s.errFunName = "54000011", "Fail", "ASR_SVC_RECG_CLOS" + } + derr = icserror.ICSERRTTSNotInit + // s.SendStasticInfo(s.callID, s.custID, s.trid, "STT", "ASR_SVC_RECG_CLOS", "54000001", "Fail", "", "", s.reqTime, time.Now(), s.sreserved, svcReqTime, time.Now()) + } + + // Server Close + l.Printf("Close STT SVC[%d]", int(s.sttID)) + rc = C.ASR_SVC_CLOS(&s.STTInfo.LVCSR_SOCK_HEAD) + //ljhwan + //rc = 0 + if int(rc) < 0 { + l.Printf("Failed to close STT SVC[%d], rc: %d", int(s.sttID), rc) + derr = icserror.ICSERRTTSNotInit + if strings.Compare(s.errCode, "54000013") != 0 { + s.errCode, s.errMsg, s.errFunName = "54000012", "Fail", "ASR_SVC_CLOS" + } + // s.SendStasticInfo(s.callID, s.custID, s.trid, "STT", "ASR_SVC_CLOS", "54000001", "Fail", "", "", s.reqTime, time.Now(), s.sreserved, s.svcReqTime, time.Now()) + // return derr + } + l.Printf("Closed STT[%d] sock header: %+v", int(s.sttID), s.STTInfo.LVCSR_SOCK_HEAD.uSock) + + s.rrData.Clear() + + l.Printf("Selvas STT Close - %d. %s", s.voiceBufCur, string(debug.Stack())) + + return localPath, URL, derr +} + +func (s *STTSelvas) GetSTTStatus() bool { + if s == nil { + // fmt.Println("get status: stt handle nil") + return false + } + s.M.Lock() + b := s.IsClose + s.M.Unlock() + + return b +} + +type AsyncCBFunc func(sttResult string, epd int, inout bool) + +func (s *STTSelvas) SendSTT(voicedata []byte, final bool, cbFunc AsyncCBFunc) (NewSTTResult, *icserror.IcsError) { + l := icslog.InitializeLogger() + var res NewSTTResult + var derr *icserror.IcsError = nil + var result string + // var sendCount int + + if s == nil { + derr = icserror.ICSERRTTSNotInit + l.Println("STTSelvas struct is not initialized") + return res, derr + } + + // // setting the language + // cLanguage := C.CString(language) + // defer C.free(unsafe.Pointer(cLanguage)) + // rc := C.ASR_SVC_SET_PARAM(&s.sttInfo.LVCSR_SOCK_HEAD, C.LVCSR_PARAM_TYPE_LANGUAGE, cLanguage) + // if rc != 0 { + // return "", icserror.ICSERRSTTLanguageSetting + // } + + defer func() { + if err := recover(); err != nil { + switch v := err.(type) { + case error: + icserror.ICSERRSTTSendFail.SetError(v) + l.Printf("PANIC! %s\n%s", icserror.ICSERRSTTSendFail.GetError().Error(), string(debug.Stack())) + default: + l.Print(icserror.ICSERRSTTSendFail.GetError().Error()) + } + } + derr = icserror.ICSERRSTTSendFail + }() + + s.uEndOfSpeech = 1 + + //recording audio + if *s.RecordStart { + s.rrData.AddAudio(voicedata) + } else { + s.rrData.AddTempAudio(voicedata) + } + + csUDataSize := C.long(s.uDataSize) + csUEndSpeech := C.long(s.uEndOfSpeech) + csBuff := (*C.char)(unsafe.Pointer(&voicedata)) + //csBuff := (*C.char)(unsafe.Pointer(&buff1[0])) + rc := C.ASR_SVC_RECG_DATA(&s.STTInfo.LVCSR_SOCK_HEAD, + csBuff, + csUDataSize, + csUEndSpeech, + &s.STTInfo.LVCSR_EPD_INFO) + + if int(rc) < 0 { + l.Printf("[%d] ASR_SVC_RECG_DATA ERROR %d", int(s.sttID), rc) + ////////////////테스트코드///////////////////// + rc = C.ASR_SVC_GET_ERROR(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_ERROR_RESULT) + if int(rc) < 0 { + l.Printf("[%d] ASR_SVC_GET_ERROR Fail.. %d", int(s.sttID), rc) + // return "", derr + } else { + l.Printf("[%d] LVCSR_ERROR_RESULT - [%d]%s", int(s.sttID), s.STTInfo.LVCSR_ERROR_RESULT.nErrorCode, C.GoString(&s.STTInfo.LVCSR_ERROR_RESULT.pErrorMsg[0])) + } + /////////////////////////////////////////////// + + s.errCode, s.errMsg, s.errFunName = "54000013", "Fail", "ASR_SVC_RECG_DATA" + if cbFunc != nil { + cbFunc("", 99, true) + } + derr = icserror.ICSERRSTTSendFail + return res, derr + } + + s.txNum++ + + rc = C.ASR_SVC_RECG_STR_PROC(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_RESULT) + s.svcRspTime = time.Now() + + if int(rc) < 0 { + l.Printf("[%d]!!!! ASR_SVC_RECG_STR_PROC Fail - rc:%d, s.STTInfo.LVCSR_RECOG_RESULT:%+v", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_RESULT) + // s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "ASR_SVC_RECG_STR_PROC" + s.result = "$NO_RESULT$" + derr = icserror.ICSERRSTTSendFail + if cbFunc != nil { + cbFunc("", 2, true) + } + return res, icserror.ICSERRSTTContinue // 묵음으로 처리 + } + + result = C.GoString(s.STTInfo.LVCSR_RECOG_RESULT.pResult) + l.Printf("STT ID[%d] result: %s, result: %p, pointer: %p", int(s.sttID), result, &result, s.STTInfo.LVCSR_RECOG_RESULT.pResult) + results := "" + if s.STTInfo.LVCSR_RECOG_RESULT.nResultLen == 0 { // 길이 0일때 값 nil로 나옴 + result = "" + } + //result = "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST" + s.result = result + results = result + if result == "$NO_RESULT$" { + results = "" + } + if cbFunc != nil { + cbFunc(results, int(s.STTInfo.LVCSR_EPD_INFO), true) + } + + // jiyounc + // return "", icserror.ICSERRSTTContinue + + res = NewSTTResult{ + Res: results, + NStart: int(s.STTInfo.LVCSR_DATA_RESULT.nStart), + NEnd: int(s.STTInfo.LVCSR_DATA_RESULT.nEnd), + } + + return res, nil +} + +// result return +func NewSTTSResult(result string, err *icserror.IcsError) *STTSResult { + return &STTSResult{result, err} +} + +// 말을 하지 않았을때 결과 조회 +func (s *STTSelvas) GetSTTResultToStopSTT() (string, *icserror.IcsError) { + l := icslog.InitializeLogger() + if s == nil { + return "", icserror.ICSERRSTTNotInit + } + + l.Printf("[%d] LVCSR_RECOG_RESULT: %+v, LVCSR_RECOG_RESULT:%+v", int(s.sttID), &s.STTInfo.LVCSR_RECOG_RESULT, s.STTInfo.LVCSR_RECOG_RESULT) + rc := C.ASR_SVC_RECG_STR_PROC(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_RESULT) + if int(rc) < 0 { + l.Printf("[%d] ASR_SVC_RECG_STR_PROC Fail - rc:%d, s.STTInfo.LVCSR_RECOG_RESULT:%+v", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_RESULT) + s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "ASR_SVC_RECG_STR_PROC" + // s.SendStasticInfo(s.callID, s.custID, s.trid, "STT", "ASR_SVC_RECG_STR_PROC", "54000001", "Fail", "", "", s.reqTime, time.Now(), s.sreserved, svcReqTime, time.Now()) + return "", icserror.ICSERRSTTSendFail + } + return "", icserror.ICSERRSTTContinue +} + +// epd 1 이후 end 전송 => 결과조회 +func (s *STTSelvas) SendSTTProcGetResult() (string, *icserror.IcsError) { + l := icslog.InitializeLogger() + var derr *icserror.IcsError = nil + if s == nil { + return "", icserror.ICSERRSTTNotInit + } + + // buff1 := make([]byte, 0) + s.uEndOfSpeech = 1 + uDataSize := 0 + csUDataSize := C.long(uDataSize) + csUEndSpeech := C.long(s.uEndOfSpeech) + // csBuff := nil + + l.Printf("[%d] LVCSR_RECOG_RESULT: %+v, LVCSR_RECOG_RESULT:%+v", int(s.sttID), &s.STTInfo.LVCSR_RECOG_RESULT, s.STTInfo.LVCSR_RECOG_RESULT) + // svcReqTime := time.Now() + rc := C.ASR_SVC_RECG_DATA(&s.STTInfo.LVCSR_SOCK_HEAD, + nil, + csUDataSize, + csUEndSpeech, + &s.STTInfo.LVCSR_EPD_INFO) + if int(rc) < 0 { + l.Printf("[%d] ASR_SVC_RECG_STR_PROC Fail - rc:%d, s.STTInfo.LVCSR_RECOG_RESULT:%+v", int(s.sttID), rc, s.STTInfo.LVCSR_RECOG_RESULT) + derr = icserror.ICSERRSTTSendFail + s.errCode, s.errMsg, s.errFunName = "54000009", "Fail", "ASR_SVC_RECG_STR_PROC" + // s.SendStasticInfo(s.callID, s.custID, s.trid, "STT", "ASR_SVC_RECG_DATA", "54000001", "Fail", "", "", s.reqTime, time.Now(), s.sreserved, svcReqTime, time.Now()) + return "", derr + } + + s.GetSTTResultToStopSTT() + // l.Print(icslog.LOG_LEVEL_INFO, s.sessionID, "Request Recg Result") + // rc = C.ASR_SVC_RECG_PROC(&s.STTInfo.LVCSR_SOCK_HEAD, &s.STTInfo.LVCSR_RECOG_RESULT) + // if int(rc) < 0 { + // derr = icserror.ICSERRSTTFail + // return "", derr + // } + return "", nil +} + +func (s STTSelvas) GetAudio() []byte { + return s.rrData.GetAudio() +} + +func (s STTSelvas) GetReqTime() time.Time { + return s.reqTime +} + +func (s *STTSelvas) CloseChanelAndServer() (bool, *icserror.IcsError) { + // 서버 결과에 사용된 메모리 초기화 + rc := C.ASR_SVC_RECG_PROC_FREE(&s.STTInfo.LVCSR_RECOG_RESULT) + if int(rc) < 0 { + l.Println("Error occured while executing ASR_SVC_RECG_PROC_FREE()") + return false, icserror.ICSERRSTTConnectCloseFail + } + // 채널 해제 + rc = C.ASR_SVC_RECG_CLOS(&s.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Println("Error occured while executing ASR_SVC_RECG_CLOS()") + return false, icserror.ICSERRSTTConnectCloseFail + } + // stt 서버 종료 + rc = C.ASR_SVC_CLOS(&s.STTInfo.LVCSR_SOCK_HEAD) + if int(rc) < 0 { + l.Println("Error occured while executing ASR_SVC_CLOS()") + return false, icserror.ICSERRSTTConnectCloseFail + } + s.rrData.Clear() + return true, nil + +}