Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
solaoi committed Jul 14, 2023
0 parents commit 45714d7
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 0 deletions.
82 changes: 82 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: nimreleaser

on:
release:
types:
- published

env:
APP_NAME: 'voicepeaky'
NIM_VERSION: 'stable'
MAINTAINER: 'solaoi'

jobs:
build-artifact:
runs-on: ${{ matrix.os.name }}
strategy:
matrix:
os:
- name: ubuntu-latest
platform: linux
cpu: amd64
- name: macOS-latest
platform: darwin
cpu: amd64
steps:
- uses: actions/checkout@v1
- uses: jiro4989/setup-nim-action@v1
with:
nim-version: ${{ env.NIM_VERSION }}
- run: nimble build -Y -d:release --threads:on
- name: Create artifact
run: |
assets="${{ env.APP_NAME }}_${{ matrix.os.platform }}_${{ matrix.os.cpu }}"
tar czf "$assets.tar.gz" "${{ env.APP_NAME }}"
shell: bash
- uses: actions/upload-artifact@v2
with:
name: artifact-${{ matrix.os.name }}
path: |
*.tar.gz
upload-release:
runs-on: ubuntu-latest
needs: build-artifact
strategy:
matrix:
include:
- os: ubuntu-latest
asset_name_suffix: linux_amd64.tar.gz
asset_content_type: application/gzip
- os: macOS-latest
asset_name_suffix: darwin_amd64.tar.gz
asset_content_type: application/gzip
steps:
- uses: actions/download-artifact@v2
with:
name: artifact-${{ matrix.os }}

- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ${{ env.APP_NAME }}_${{ matrix.asset_name_suffix }}
asset_name: ${{ env.APP_NAME }}_${{ matrix.asset_name_suffix }}
asset_content_type: ${{ matrix.asset_content_type }}

homebrew:
runs-on: ubuntu-latest
needs: upload-release
steps:
- name: Update Homebrew Formula
uses: izumin5210/action-homebrew-tap@releases/v0
with:
tap: solaoi/homebrew-tap
token: ${{ secrets.GITHUB_TOKEN }}
tap-token: ${{ secrets.TAP_GITHUB_TOKEN }}
release-branch: main
formula: voicepeaky.rb
commit-message: Brew formula update for voicepeaky version ${{ env.RELEASE_VERSION }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
voicepeaky
*.json
!sample.json
.DS_Store
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 solaoi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
92 changes: 92 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Voicepeaky

[![license](https://img.shields.io/github/license/solaoi/voicepeaky)](https://github.com/solaoi/voicepeaky/blob/main/LICENSE)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/solaoi/voicepeaky)](https://github.com/solaoi/voicepeaky/releases)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/solaoi?color=db61a2)](https://github.com/sponsors/solaoi)

This is a server to use voicepeak as api.

## Requirements

- [VOICEPEAK 商用可能 6ナレーターセット](https://www.ah-soft.com/voice/6nare/index.html)
- [VOICEPEAK 商用可能 ナレーター](https://www.ah-soft.com/voice/narrator/index.html)

## Usage

### Serve

```sh
voicepeaky -p 9999 -s
```

Option is below.

| Option | Description |
| --------- | ---------------------------------------- |
| -p,--port | specify the port you want to serve |
| -s,--skip | skip old text when new text is requested |

### Request

```sh
curl -X POST -H "Content-Type: application/json" -d '@sample.json' localhost:9999
```

RequestBody (JSON Format) is below.
see a sample [here](https://raw.githubusercontent.com/solaoi/voicepeaky/main/sample.json).

| Field | Type | Sample |
| ------------- | ----------------------- | --------------------- |
| - (parent) | JSONObject | - |
| narrator | string*1 | "Japanese Male Child" |
| emotion | JSONObject | - |
| emotion/happy | number(0 - 100) | 0 |
| emotion/fun | number(0 - 100) | 0 |
| emotion/angry | number(0 - 100) | 0 |
| emotion/sad | number(0 - 100) | 0 |
| speed | number(50 - 200) | 100 |
| pitch | number(-300 - 300) | 0 |
| text | string | "こんにちは" |

*1
| Types of Narrators | Requirements |
| --------------------- | ---------------------------------------------------------------------------------- |
| Japanese Male Child | [VOICEPEAK 商用可能 ナレーター](https://www.ah-soft.com/voice/narrator/index.html) |
| Japanese Female Child | [VOICEPEAK 商用可能 6ナレーターセット](https://www.ah-soft.com/voice/6nare/index.html) |
| Japanese Male 1 | [VOICEPEAK 商用可能 6ナレーターセット](https://www.ah-soft.com/voice/6nare/index.html) |
| Japanese Male 2 | [VOICEPEAK 商用可能 6ナレーターセット](https://www.ah-soft.com/voice/6nare/index.html) |
| Japanese Male 3 | [VOICEPEAK 商用可能 6ナレーターセット](https://www.ah-soft.com/voice/6nare/index.html) |
| Japanese Male 4 | [VOICEPEAK 商用可能 ナレーター](https://www.ah-soft.com/voice/narrator/index.html) |
| Japanese Female 1 | [VOICEPEAK 商用可能 6ナレーターセット](https://www.ah-soft.com/voice/6nare/index.html) |
| Japanese Female 2 | [VOICEPEAK 商用可能 6ナレーターセット](https://www.ah-soft.com/voice/6nare/index.html) |
| Japanese Female 3 | [VOICEPEAK 商用可能 6ナレーターセット](https://www.ah-soft.com/voice/6nare/index.html) |
| Japanese Female 4 | [VOICEPEAK 商用可能 ナレーター](https://www.ah-soft.com/voice/narrator/index.html) |

## Install

### 1. Mac

```
# Install
brew install solaoi/tap/voicepeaky
# Update
brew upgrade voicepeaky
```

### 2. BinaryRelease

```sh
# Install with wget or curl
## set the latest version on releases.
VERSION=v1.0.0
## set the OS you use. (macos)
OS=linux
## case you use wget
wget https://github.com/solaoi/voicepeaky/releases/download/$VERSION/voicepeaky${OS}.tar.gz
## case you use curl
curl -LO https://github.com/voicepeaky/broly/releases/download/$VERSION/voicepeaky${OS}.tar.gz
## extract
tar xvf ./voicepeaky${OS}.tar.gz
## move it to a location in your $PATH, such as /usr/local/bin.
mv ./voicepeaky /usr/local/bin/
```
14 changes: 14 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Security Policy

## Supported Versions

There is only one version track for voicepeaky so all patches will be applied to the latest version only.

| Version | Supported |
| ------- | ------------------ |
| Latest | :white_check_mark: |

## Reporting a Vulnerability

Critical vulnerabilities can be responsibly disclosed to [mail@aota.blog](mailto:mail@aota.blog).
Bugs and low level vulneratbilities can be reported via repository issues.
1 change: 1 addition & 0 deletions nim.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--threads:on
12 changes: 12 additions & 0 deletions sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"narrator": "Japanese Female Child",
"emotion": {
"happy": 0,
"fun": 0,
"angry": 0,
"sad": 0
},
"speed": 100,
"pitch": 0,
"text": "こんにちは"
}
28 changes: 28 additions & 0 deletions src/speech.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import json,strformat

type Speech* = ref object
narrator*: string
happy*: int
fun*: int
angry*: int
sad*: int
speed*: int
pitch*: int
text*: string

type
SpeechInitError* = object of IOError

proc createSpeech*(json:JsonNode, text:string) :Speech=
result = new Speech
try:
result.narrator = json["narrator"].getStr
result.happy = json["emotion"]["happy"].getInt
result.fun = json["emotion"]["fun"].getInt
result.angry = json["emotion"]["angry"].getInt
result.sad = json["emotion"]["sad"].getInt
result.speed = json["speed"].getInt
result.pitch = json["pitch"].getInt
result.text = text
except:
raise newException(SpeechInitError, fmt"request body is invalid")
89 changes: 89 additions & 0 deletions src/voicepeaky.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import httpbeast,os,strutils,parseopt,json,asyncdispatch,options,threadpool,times,algorithm,osproc
import speech

let workspace = "/tmp/voicepeaky"
var speeches: seq[Speech]
var is_skip: bool

proc getArgs():tuple[port:int, skip:bool] =
result = (port:8080, skip:false)
var opt = parseopt.initOptParser( os.commandLineParams().join(" ") )
for kind, key, val in opt.getopt():
case key
of "port", "p":
case kind
of parseopt.cmdLongOption, parseopt.cmdShortOption:
opt.next()
result.port = opt.key.parseInt()
else: discard
of "skip", "s":
result.skip = true

proc onRequest(req: Request): Future[void]{.async.} =
var isSend:bool
{.cast(gcsafe).}:
if req.httpMethod == some(HttpPost):
try:
let requestBody = req.body.get.parseJson

let text = requestBody["text"].getStr
var textArr: seq[Speech]
for line in splitLines(text):
for temp in line.split(""):
for value in temp.split(""):
if not value.isEmptyOrWhitespace():
let speech = createSpeech(requestBody, value)
textArr.add(speech)
if is_skip:
speeches = @[]
speeches = @textArr.reversed & @speeches

req.send(Http201)
except Exception:
let
headers = "Content-type: application/json; charset=utf-8"
response = %*{"message": "Error occurred."}
req.send(Http400, $response, headers)
finally:
isSend=true
break
if not isSend:
req.send(Http404)

proc pollingFiles() {.thread.} =
{.cast(gcsafe).}:
while true:
var files: seq[string]
for f in walkDir(workspace):
files.add(f.path)
files.sort();
for file in files:
let _ = execCmd("afplay " & file)
removeFile(file)

proc pollingSpeeches() {.thread.} =
{.cast(gcsafe).}:
while true:
if speeches.len != 0:
let speech = speeches.pop()
let _ = execCmd("/Applications/voicepeak.app/Contents/MacOS/voicepeak --say " & speech.text &
" --narrator \"" & speech.narrator &
"\" --emotion happy=" & $speech.happy &
",fun=" & $speech.fun &
",angry=" & $speech.fun &
",sad=" & $speech.fun &
" --speed " & $speech.speed &
" --pitch " & $speech.pitch &
" --out " & workspace & "/" & $now() & ".wav")

when isMainModule:
if not dirExists(workspace):
createDir(workspace)

spawn pollingSpeeches()
spawn pollingFiles()

let (port, skip) = getArgs()
is_skip = skip
let settings = initSettings(Port(port))
run(onRequest, settings)
14 changes: 14 additions & 0 deletions voicepeaky.nimble
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Package

version = "1.0.0"
author = "solaoi"
description = "Voicepeak Server"
license = "MIT"
srcDir = "src"
bin = @["voicepeaky"]


# Dependencies

requires "nim >= 1.6.10"
requires "httpbeast >= 0.4.1"

0 comments on commit 45714d7

Please sign in to comment.