Search

[Java] OpenAI Whisper API 자바 예제 코드

Last update: @6/12/2023

Whiper API

OpenAI의 AI 음성 인식 및 번역 API로, 유튜브 자동 자막 또는 그 이상의 성능을 보여줌
음성 인식 API - 예제에서 사용
https://api.openai.com/v1/audio/transcriptions
Plain Text
복사
특별히 언어를 지정하지 않아도 자동으로 언어를 인식해서 텍스트로 바꿔줌
특정 언어를 지정하면 다른 언어로 말을 해도 지정 언어로 바꿔줌(번역 API와 똑같이 동작함)
번역 API - 모든 언어를 영어로 번역해줌
https://api.openai.com/v1/audio/translations
Plain Text
복사

Java

스프링 MVC 사용
API 키는 이곳에서 발급 가능함(API 사용은 유료)
컨트롤러 - 음성파일을 수신해서 openAI API로 넘겨줌
@ResponseBody @PostMapping("/whisper") public String transcript( @RequestParam MultipartFile audio ) { return sttClient.transcript(audio); }
Java
복사
OpenAi 클래스 (HttpURLConnection 이용)
@Component @Slf4j public class WhisperClient implements SttClient { @Value("${api-key.open-ai}") private String API_KEY; private final String API_URL = "https://api.openai.com/v1/audio/transcriptions"; @Override public String transcript(MultipartFile audio) { return transcript(audio, "en"); } @Override public String transcript(MultipartFile audio, String language) { // Speech To Text(whisper API) String script = ""; try { // HTTP 통신 설정 URL url = new URL(API_URL); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setUseCaches(false); con.setDoOutput(true); con.setDoInput(true); String LINE_FEED = "\r\n"; String boundary = "----" + UUID.randomUUID(); con.setRequestProperty("Authorization", "Bearer " + API_KEY); con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); OutputStream os = con.getOutputStream(); PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8), true); // request body writer.append("--" + boundary).append(LINE_FEED); writer.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + "audio.webm" + "\"").append(LINE_FEED); writer.append(LINE_FEED); writer.flush(); InputStream is = audio.getInputStream(); BufferedInputStream bis = new BufferedInputStream(is); byte[] buffer = new byte[4096]; int bytesRead = -1; while ((bytesRead = bis.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } os.flush(); bis.close(); writer.append(LINE_FEED); writer.append("--" + boundary).append(LINE_FEED); writer.append("Content-Disposition: form-data; name=\"model\"").append(LINE_FEED); writer.append(LINE_FEED); writer.append("whisper-1").append(LINE_FEED); writer.append("--" + boundary).append(LINE_FEED); writer.append("Content-Disposition: form-data; name=\"language\"").append(LINE_FEED); writer.append(LINE_FEED); writer.append("en").append(LINE_FEED); writer.append("--" + boundary + "--").append(LINE_FEED); writer.close(); // 결과 수신 String output = new BufferedReader(new InputStreamReader(con.getInputStream())) .lines() .reduce((a, b) -> a + b) .orElse(""); if (output.equals("")) { return ""; } log.info("transcript output = {}", output); JSONObject jsonObject = new JSONObject(output); script = jsonObject.getString("text"); } catch (Exception e) { throw new ApiFailException(e); } return script; } }
Java
복사
Authorization 헤더는 Bearer가 포함되어야 하고, 띄어쓰기 후 API 키가 들어감
파라미터는 file, model 두 가지가 필수이고 나머지 prompt, response_format, temperature, language 파라미터가 옵션으로 들어갈 수 있음. 자세한 것은 API 문서 참고
전송되는 HTTP 전문은 아래와 같음
POST /v1/audio/transcriptions HTTP/1.1 Authorization: Bearer my_api_key User-Agent: Java/17.0.6 Accept: */* Host: api.openai.com Accept-Encoding: gzip, deflate, br Connection: keep-alive Content-Type: multipart/form-data; boundary=----2174e5e9-b18e-49f4-9a39-4024757ef25c Content-Length: 12365 ------2174e5e9-b18e-49f4-9a39-4024757ef25c Content-Disposition: form-data; name="file"; filename="c9781b97-8cd6-4134-a91b-1ad43d100902.webm" <binary data> ------2174e5e9-b18e-49f4-9a39-4024757ef25c Content-Disposition: form-data; name="model" whisper-1 ------2174e5e9-b18e-49f4-9a39-4024757ef25c--
Plain Text
복사

View (HTML, Javascript)

대충 아래와 같은 화면에 녹음 및 출력을 해주는 페이지
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>OpenAI Whisper</title> </head> <body onload="mediaStart()"> <h1>whisper</h1> <input type="button" id="record" value="녹음 시작"> <input type="button" id="stop" value="녹음 중지"> <textarea rows="3" id="textarea"></textarea> <script> // 음성 녹음 function mediaStart() { let recordButton = document.querySelector("#record"); let stopButton = document.querySelector("#stop"); if (navigator.mediaDevices) { const constraints = {audio:true}; let chunks = [] // 녹음된 내용을 저장할 버퍼 navigator.mediaDevices.getUserMedia({audio:true}).then(stream => { // 녹음을 시작하고 종료하는 객체 const mediaRecorder = new MediaRecorder(stream, {mimeType:'audio/webm'}); // 녹음 시작 recordButton.onclick = () => { chunks = []; // 이전 녹음 초기화 mediaRecorder.start(); recordButton.style.backgroundColor = "red"; recordButton.style.color = "white"; recordButton.disabled = true; } // 오디오 저장 mediaRecorder.ondataavailable = e => { chunks.push(e.data); } // 녹음 종료 stopButton.onclick = () => { mediaRecorder.stop(); recordButton.style.backgroundColor = ""; recordButton.style.color = ""; recordButton.disabled = false; } // 녹음이 종료되면 서버로 녹음 내용을 보내고 결과 수신 mediaRecorder.onstop = () => { // chunks에 저장된 데이터를 바이너리코드로 변환 const blob = new Blob(chunks, {'type':'audio/webm'}); // 서버로 전송 let formData = new FormData(); formData.append("audio", blob); ajax(formData); } }).catch(err => { console.log(err); }); } else { console.log("미디어 장치 없음"); } } function ajax(formData) { let request = new XMLHttpRequest(); request.onload = () => { let responseText = request.responseText; console.log(responseText); document.querySelector('#textarea').innerText = responseText; } request.open("POST", "/whisper"); request.send(formData); } </script> </body> </html>
JavaScript
복사

테스트

중간 중간 영어를 섞어도 알아서 바꿔줌
마침표도 찍어주기 때문에 문장별로 파싱하기도 좋음.

Spring WebClient 이용 (@6/12/2023 추가)

스프링 5부터 지원하는 WebClient를 이용하면 코드를 간결하게 작성할 수 있음
스프링 RestTemplate는 향후 deprecated될 예정
@Component @Slf4j public class WhisperClient implements SttClient { @Value("${api-key.open-ai}") private String API_KEY; private final String API_URL = "https://api.openai.com/v1/audio/transcriptions"; private WebClient webClient; @PostConstruct private void init() { webClient = WebClient.builder() .baseUrl(API_URL) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + API_KEY) .build(); } @Override public String transcript(MultipartFile audio) { return transcript(audio, "en"); } @Override public String transcript(MultipartFile audio, String language) { try { MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder(); bodyBuilder.part("file", new InputStreamResource(audio.getInputStream())) .filename("audio.webm"); bodyBuilder.part("model", "whisper-1"); bodyBuilder.part("language", language); String result = webClient .post() .body(BodyInserters.fromMultipartData(bodyBuilder.build())) .retrieve() .bodyToMono(String.class) .block(); return new JSONObject(result).getString("text"); } catch (Exception e) { log.error(e.getMessage()); throw new ApiFailException(e); } } }
Java
복사

References

관련 문서