You need to enable JavaScript to run this app.
导航
接口说明
最近更新时间:2024.08.15 16:43:36首次发布时间:2021.09.06 10:48:04

接入必读

请先查看接入必读了解具体接入方式,再参考此文档完成接入。

功能介绍

DeepChorus为用户提供副歌检测能力,自动分析获取音乐的段落结构,发现音乐的副歌高潮部分,可用于音乐视频高光剪辑等场景。

  • 输入:音频片段
  • 输出:副歌检测的结果

接口说明

  • 当前支持通过 HTTP 协议在线调用。
  • 请求内容包括:
    • payload字段为将请求参数序列化后的json文本
    • data字段为将音频二进制文件按照base64格式编码(标准base64,RFC 4648)的文本
  • 使用备注:
注意项说明

功能

限制说明

  • 模型适用于 一般流行( pop )歌曲 ,音乐结构单纯,常用结构套路顺序多为:前奏、主歌、副歌、主歌、副歌、间奏、副歌、尾奏。相对于其他段落,副歌通常具有比较高的能量和音高,在旋律、节奏和情感上均与主歌形成反差,为歌曲曲调提供变化性。副歌重复性强,使人容易记忆,朗朗上口,一般流行歌曲多会重复二到三次副歌,增加记忆性。因此,副歌又可称为一首歌的商标,当无法呼出歌名时,多会哼唱副歌弦律来表达歌曲。
  • 对于其他音乐风格,例如爵士(jazz)、古典(classical)、世界音乐(world),其结构较无法用“主歌-副歌”这种结构归纳,模型较无法准确判断副歌位置
  • 对于短片段音频,例如抖音上常见、小于30秒的片段,基本上已经多是剪好的高潮副歌片段,模型便无法再检测出副歌片段
  • 输入歌曲尽量为全曲,或是至少包含一段主歌和副歌的结构,时长大概在1分30秒以上,模型较能够准确判断
  • 避免直接拼接json文本,尽量使用转换库,避免造成转义符等导致json格式错误
输入
音频格式支持
wav、pcm、mp3、aac等常见格式
音频编码建议采样率大于等于16kHz,否则将进行自动转码,可能带来效果损失和更多耗时处理
音频时长限制小于等于10分钟;建议大于15s,否则返回全曲高潮
音频大小限制小于等于100MB
输出结果格式json字符串格式,详情请参考响应格式

公共参数

参考详细说明功能调用-通用协议

配置参数

payload配置参数为json字符串格式

字段描述类型是否必传默认值
url服务请求数据的url,若data字段为空,则使用该url下载音频数据。详见功能调用-通用协议-payload.urlstring-
audio_info音频参数,便于服务节省音频解码耗时object-
audio_info.format音频编码格式,wav/mp3/aacstring-
audio_info.sample_rate音频采样率number-
audio_info.channel音频通道数number-

响应格式

HTTP响应Content-Type: application/json

字段描述类型
task_id请求任务id,用于链路追踪、问题排查string
namespace服务接口命名空间,比如DeepChorusstring
data请求响应二进制数据,标准base64编码string
payload请求响应文本信息,json字符串格式string
status_code状态码number
status_text状态信息string
  • 响应结果payload为json字符串格式,json内容格式如下:
    payload包含三个对象字段:"thumbnail" (高潮)、"chorus_segments"(副歌)、"raw_segments"(原始段落)。每个对象存的是一个list,结构包含以下字段:

    • interval: 段落的起点、终点秒数

      • start_prob: 起点的概率分数

      • end_prob: 终点的概率分数

      • chorus_prob: 整段落的副歌概率分数

      • duration: 段落的长度秒数

      • res_duration: 起点到整曲结束剩的余长度秒数

      字段描述类型
      thumbnail置信度最大的一段高潮array
      thumbnail.interval段落的起点、终点秒数array
      thumbnail.start_prob起点的概率分数number
      thumbnail.end_prob终点的概率分数number
      thumbnail.chorus_prob整段落的副歌概率分数number
      thumbnail.duration段落的长度秒数number
      thumbnail.res_duration起点到整曲结束剩的余长度秒数number
      chorus_segments副歌array
      chorus_segments.*子结构的字段同thumbnail-
      raw_segments原始段落array
      raw_segments.*子结构的字段同thumbnail-

      响应结果payload示例:

      {
        "frame_sec": 0.192,
        "thumbnail": [
          {
            "start_prob": 0.099719256,
            "end_prob": 0.04581437,
            "duration": 37.248,
            "interval": [
              212.73601,
              249.98401
            ],
            "res_duration": 57.024002,
            "chorus_prob": 0.86254287
          }
        ],
        "chorus_segments": [
          {
            "start_prob": 0.09299248,
            "end_prob": 0.02493893,
            "duration": 35.712,
            "interval": [
              54.528,
              90.24
            ],
            "res_duration": 215.232,
            "chorus_prob": 0.83274376
          },
          {
            "start_prob": 0.074075386,
            "end_prob": 0.037723586,
            "duration": 48.768,
            "interval": [
              117.888,
              166.656
            ],
            "res_duration": 151.872,
            "chorus_prob": 0.82471144
          },
          {
            "start_prob": 0.099719256,
            "end_prob": 0.04581437,
            "duration": 37.248,
            "interval": [
              212.73601,
              249.98401
            ],
            "res_duration": 57.024002,
            "chorus_prob": 0.86254287
          }
        ],
        "raw_segments": [
          {
            "start_prob": 0.055697553,
            "end_prob": 0.09299248,
            "duration": 0,
            "interval": [
              39.744,
              54.528
            ],
            "res_duration": 0,
            "chorus_prob": 0.31311455
          },
          ...
          {
            "start_prob": 0.049528554,
            "end_prob": 0.042224556,
            "duration": 0,
            "interval": [
              268.41602,
              269.568
            ],
            "res_duration": 0,
            "chorus_prob": 0.10143171
          }
        ]
      }
      
      

参考示例

调用方式为:POST /api/v1/invoke

Golang

// Code sample:
// use http client to invoke SAMI HTTP Service
package main

import (
   "bytes"
   "encoding/base64"
   "encoding/json"
   "fmt"
   "io/ioutil"
   "log"
   "net/http"
   "time"
)

type InvokeResponse struct {
   StatusCode int32   `form:"status_code,required" json:"status_code,required" query:"status_code,required"`
   StatusText string  `form:"status_text,required" json:"status_text,required" query:"status_text,required"`
   TaskId     string  `form:"task_id,required" json:"task_id,required" query:"task_id,required"`
   Namespace  string  `form:"namespace,required" json:"namespace,required" query:"namespace,required"`
   Payload    *string `form:"payload,omitempty" json:"payload,omitempty" query:"payload,omitempty"`
   Data       []byte  `form:"data,omitempty" json:"data,omitempty" query:"data,omitempty"`
   State      *string `form:"state,omitempty" json:"state,omitempty" query:"state,omitempty"`
}

const (
   domain = "https://sami.bytedance.com"

   // auth token
   appkey    = "your_appkey"

   // SAMI method
   version   = "v4"
   namespace = "DeepChorus"

   // dump output
   dataOutputFile    = "output.wav"
   payloadOutputFile = "output.json"
   isDump            = true
)

func main() {
   // Get token
   token := "your_token"

   // Construct HTTP request
   //   1. Read local audio file
   //   2. Set HTTP json body
   //   3. Do HTTP POST request
   audioPath := "/path/to/audio"
   content, err := ioutil.ReadFile(audioPath)
   if err != nil {
      log.Fatalf("failed to read file: %v", err)
   }
   data := base64.StdEncoding.EncodeToString(content)
   body := fmt.Sprintf(`{"data": "%v"}`, data)
   urlPath := fmt.Sprintf(
      "%v/api/v1/invoke?version=%v&token=%v&appkey=%v&namespace=%v",
      domain, version, token, appkey, namespace,
   )
   log.Printf("invoke request: %v", urlPath)

   // HTTP POST request
   start := time.Now()
   resp, err := http.Post(urlPath, "application/json", bytes.NewBuffer([]byte(body)))
   if err != nil {
      panic(err)
   }
   defer resp.Body.Close()

   // Parse HTTP response
   ret, err := ioutil.ReadAll(resp.Body)
   if err != nil || resp.StatusCode != http.StatusOK {
      panic(string(ret))
   }
   log.Printf("http invoke: cost=%vms, response: %v", time.Since(start).Milliseconds(), string(ret))

   // parse SAMI response
   samiResp := InvokeResponse{}
   payloadStr := ""
   if err = json.Unmarshal(ret, &samiResp); err != nil {
      log.Println("parse response failed", string(ret), err)
      panic(err)
   }
   if samiResp.Payload != nil {
      payloadStr = *samiResp.Payload
   }
   log.Printf("response task_id=%v, payload=%v, data=[%d]byte", samiResp.TaskId, payloadStr, len(samiResp.Data))
   if isDump && samiResp.Payload != nil {
      _ = ioutil.WriteFile(payloadOutputFile, []byte(*samiResp.Payload), 0644)
   }
   if isDump && len(samiResp.Data) > 0 {
      _ = ioutil.WriteFile(dataOutputFile, samiResp.Data, 0644)
   }
}

常见问题