qtxie / notes

0 stars 0 forks source link

Low-level Audio in R/S #5

Open qtxie opened 4 years ago

qtxie commented 4 years ago

API详细说明参考:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1386r2.pdf

目前我们只支持 Sample Type float32! 就行。三大平台应该都支持这个格式。

各平台使用的框架:

计划支持的功能:

;-- implementation-defined type
#case [
    OS = 'Windows [
        #define audio-buffer-size! xxx
        #define audio-sample-rate! xxx
        #define audio-device-id! xxx
        audio-clock!: alias struct! [xxx]
    ]
    OS = ...
]

#define MAX_NUM_CHANNELS        16

#enum audio-sample-type! [
    SAMPLE_TYPE_F32
    SAMPLE_TYPE_I16
]

#enum audio-device-event! [
    DEVICE_CHANGED
    DEFAULT_INPUT_CHANGED
    DEFAULT_OUTPUT_CHANGED
]

audio-buffer!: alias struct! [
    contiguous?     [logic!]
    frames-count    [integer!]
    channels-count  [integer!]
    sample-type     [audio-sample-type!]
    stride          [integer!]
    channels        [ptr-ptr!]
]

audio-device-io!: alias struct! [
    input-buffer    [audio-buffer!]
    input-time      [audio-clock!]
    output-buffer   [audio-buffer!]
    output-time     [audio-clock!]
]

audio-io-callback!: alias function! [
    dev     [audio-device!]
    io      [audio-device-io!]
]

audio-device-callback!: alias function! [
    dev     [audio-device!]
]

audio-changed-callback!: alias function! []

audio-device!: alias struct! [
    ;-- implementation-defined
    ;-- 每个实现自己定里面需要的字段
]

audio: context [
    all-devices: func [
        count   [int-ptr!]          ;-- number of devices
        return: [audio-device!]     ;-- an array of audio-device!
    ]

    input-devices: func [
        count   [int-ptr!]          ;-- number of input devices
        return: [audio-device!]     ;-- an array of audio-device!
    ]

    output-devices: func [
        count   [int-ptr!]          ;-- number of output devices
        return: [audio-device!]     ;-- an array of audio-device!
    ]

    default-input-device: func [
        return: [audio-device!]
    ]

    default-output-device: func [
        return: [audio-device!]
    ]

    set-device-changed-callback: func [
        event   [audio-device-event!]
        cb      [int-ptr!]          ;-- audio-changed-callback!
    ]

    free-device: func [
        dev     [audio-device!]
    ]

    free-devices: func [
        devs    [audio-device!]     ;-- an array of audio-device!
        count   [integer!]          ;-- number of devices
        /local
            p   [byte-ptr!]
    ][
        p: as byte-ptr! devs
        loop count [
            free-device devs
            devs: devs + 1
        ]
        free p
    ]
]

audio-device: context [
    name: func [
        dev     [audio-device!]
        return: [c-string!]
    ]

    id: func [
        dev     [audio-device!]
        return: [audio-device-id!]
    ]

    input-channels-count: func [
        dev     [audio-device!]
        return: [integer!]
    ]

    output-channels-count: func [
        dev     [audio-device!]
        return: [integer!]
    ]

    buffer-size: func [
        dev     [audio-device!]
        return: [audio-buffer-size!]
    ]

    set-buffer-size: func [
        dev     [audio-device!]
        size    [audio-buffer-size!]
        return: [logic!]
    ]

    sample-rate: func [
        dev     [audio-device!]
        return: [audio-sample-rate!]
    ]

    set-sample-rate: func [
        dev     [audio-device!]
        rate    [audio-sample-rate!]
        return: [logic!]
    ]

    input?: func [
        dev     [audio-device!]
        return: [logic!]
    ]

    output?: func [
        dev     [audio-device!]
        return: [logic!]
    ]

    running?: func [
        dev     [audio-device!]
        return: [logic!]
    ]

    sample-type-support?: func [
        dev     [audio-device!]
        return: [logic!]
    ]

    can-connect?: func [
        dev     [audio-device!]
        return: [logic!]
    ]

    can-process?: func [
        dev     [audio-device!]
        return: [logic!]
    ]

    has-unprocessed-io?: func [
        dev     [audio-device!]
        return: [logic!]
    ]

    process: func [
        dev     [audio-device!]
        type    [audio-sample-rate!]
        io-cb   [int-ptr!]      ;-- audio-io-callback!
    ]

    connect: func [
        dev     [audio-device!]
        type    [audio-sample-rate!]
        io-cb   [int-ptr!]      ;-- audio-io-callback!
    ]

    start: func [
        dev      [audio-device!]
        start-cb [int-ptr!]     ;-- audio-device-callback!
        stop-cb  [int-ptr!]     ;-- audio-device-callback!
    ]

    stop: func [
        dev     [audio-device!]
        return: [logic!]
    ]

    wait: func [
        dev     [audio-device!]     
    ]
]

audio-buffer: context [
    frames-count: func [
        buf         [audio-buffer!]
        return:     [ulong!]
    ]

    channels-count: func [
        buf         [audio-buffer!]
        return:     [ulong!]
    ]

    samples-count: func [
        buf         [audio-buffer!]
        return:     [ulong!]
    ]

    contiguous?: func [
        buf         [audio-buffer!]
        return:     [logic!]
    ]

    channels-contiguous?: func [
        buf         [audio-buffer!]
        return:     [logic!]
    ]

    frames-contiguous?: func [
        buf         [audio-buffer!]
        return:     [logic!]
    ]

    data: func [
        buf         [audio-buffer!]
        return:     [float32-ptr!]  
    ]

    write: func [
        buf         [audio-buffer!]
        frame-idx   [integer!]
        channel-idx [integer!]
        value       [float32!]
    ]

    read: func [
        buf         [audio-buffer!]
        frame-idx   [integer!]
        channel-idx [integer!]
        return:     [float32!]
    ]

    ;@@ implements those functions onces we have int16! in R/S
    ;data-i16: func [
    ;   buf         [audio-buffer!]
    ;   return:     [int16!]    
    ;]

    ;write-i16: func [
    ;   buf         [audio-buffer!]
    ;   frame-idx   [integer!]
    ;   channel-idx [integer!]
    ;   value       [int16!]
    ;]

    ;read-i16: func [
    ;   buf         [audio-buffer!]
    ;   frame-idx   [integer!]
    ;   channel-idx [integer!]
    ;   return:     [int16!]
    ;]
]

示例代码: Ports from: https://github.com/stdcpp-audio/libstdaudio/blob/master/examples/level_meter.cpp

#define COND_CC [#if OS <> 'Windows [[cdecl]]]

audio-io-cb: func [
    COND_CC
    dev     [audio-device!]
    io      [audio-device-io!]
][
    ...
]

test: func [return: [integer!]][
    device: audio/default-input-device
    if null? device [return 1]

    audio-device/connect device SAMPLE_TYPE_F32 as int-ptr! :audio-io-cb
    audio-device/start device null null

    while [audio-device/running? device][
        ;sleep 250ms

    ]

]

后续需要在这个库的基础上实现Sound API。因此在实现的过程中,在查阅系统API的文档时,可以对相关的系统API留个心眼。

Sound API大致如下:

Load-WAV
Load-MP3
Play-Sound
Stop-Sound
Pause-Sound
Loop-Sound
Set-Volume
Set-Pan
Set-Delay