1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <zircon/assert.h>
6#include <math.h>
7#include <fbl/algorithm.h>
8#include <fbl/limits.h>
9
10#include "sine-source.h"
11
12zx_status_t SineSource::Init(float freq,
13                             float amp,
14                             float duration_secs,
15                             uint32_t frame_rate,
16                             uint32_t channels,
17                             audio_sample_format_t sample_format) {
18
19    if (!frame_rate)
20        return ZX_ERR_INVALID_ARGS;
21
22    if (!channels)
23        return ZX_ERR_INVALID_ARGS;
24
25    frame_rate_ = frame_rate;
26    channels_ = channels;
27
28    frames_to_produce_ = (duration_secs == 0.0)
29                       ? fbl::numeric_limits<uint64_t>::max()
30                       : static_cast<uint64_t>(duration_secs * static_cast<float>(frame_rate_));
31    sine_scalar_ = (freq * 2.0 * M_PI) / frame_rate_;
32    amp_ = fbl::clamp<double>(amp, 0.0, 1.0);
33
34    switch(static_cast<audio_sample_format_t>(sample_format & ~AUDIO_SAMPLE_FORMAT_FLAG_MASK)) {
35    case AUDIO_SAMPLE_FORMAT_8BIT:       return InitInternal<AUDIO_SAMPLE_FORMAT_8BIT>();
36    case AUDIO_SAMPLE_FORMAT_16BIT:      return InitInternal<AUDIO_SAMPLE_FORMAT_16BIT>();
37    case AUDIO_SAMPLE_FORMAT_20BIT_IN32: return InitInternal<AUDIO_SAMPLE_FORMAT_20BIT_IN32>();
38    case AUDIO_SAMPLE_FORMAT_24BIT_IN32: return InitInternal<AUDIO_SAMPLE_FORMAT_24BIT_IN32>();
39    case AUDIO_SAMPLE_FORMAT_32BIT:      return InitInternal<AUDIO_SAMPLE_FORMAT_32BIT>();
40    default:                             return ZX_ERR_INVALID_ARGS;
41    }
42}
43
44zx_status_t SineSource::GetFormat(Format* out_format) {
45    if (out_format == nullptr)
46        return ZX_ERR_INVALID_ARGS;
47
48    out_format->frame_rate    = frame_rate_;
49    out_format->channels      = static_cast<uint16_t>(channels_);
50    out_format->sample_format = sample_format_;
51
52    return ZX_OK;
53}
54
55zx_status_t SineSource::GetFrames(void* buffer, uint32_t buf_space, uint32_t* out_packed) {
56    ZX_DEBUG_ASSERT(get_frames_thunk_ != nullptr);
57    return ((*this).*(get_frames_thunk_))(buffer, buf_space, out_packed);
58}
59
60namespace {
61
62template <audio_sample_format_t SAMPLE_FORMAT>
63struct SampleTraits;
64
65template <>
66struct SampleTraits<AUDIO_SAMPLE_FORMAT_8BIT> {
67    using SampleType   = uint8_t;
68    using ComputedType = int8_t;
69    static SampleType encode(ComputedType v) {
70        return static_cast<ComputedType>(static_cast<SampleType>(v) + 0x80);
71    }
72};
73
74template <>
75struct SampleTraits<AUDIO_SAMPLE_FORMAT_16BIT> {
76    using SampleType   = int16_t;
77    using ComputedType = int16_t;
78    static SampleType encode(ComputedType v) { return v; }
79};
80
81template <>
82struct SampleTraits<AUDIO_SAMPLE_FORMAT_20BIT_IN32> {
83    using SampleType   = int32_t;
84    using ComputedType = int32_t;
85    static SampleType encode(ComputedType v) {
86        return static_cast<SampleType>(static_cast<uint32_t>(v) & 0xFFFFF000);
87    }
88};
89
90template <>
91struct SampleTraits<AUDIO_SAMPLE_FORMAT_24BIT_IN32> {
92    using SampleType   = int32_t;
93    using ComputedType = int32_t;
94    static SampleType encode(ComputedType v) {
95        return static_cast<SampleType>(static_cast<uint32_t>(v) & 0xFFFFFF00);
96    }
97};
98
99template <>
100struct SampleTraits<AUDIO_SAMPLE_FORMAT_32BIT> {
101    using SampleType   = int32_t;
102    using ComputedType = int32_t;
103    static SampleType encode(ComputedType v) { return v; }
104};
105
106} // Anon namespace
107
108template <audio_sample_format_t SAMPLE_FORMAT>
109zx_status_t SineSource::InitInternal() {
110    using SampleType   = typename SampleTraits<SAMPLE_FORMAT>::SampleType;
111    using ComputedType = typename SampleTraits<SAMPLE_FORMAT>::ComputedType;
112
113    sample_format_ = SAMPLE_FORMAT;
114    get_frames_thunk_ = &SineSource::GetFramesInternal<SAMPLE_FORMAT>;
115    frame_size_ = static_cast<uint32_t>(sizeof(SampleType) * channels_);
116    amp_ *= fbl::numeric_limits<ComputedType>::max() - 1;
117
118    return ZX_OK;
119}
120
121template <audio_sample_format_t SAMPLE_FORMAT>
122zx_status_t SineSource::GetFramesInternal(void* buffer, uint32_t buf_space, uint32_t* out_packed) {
123    using Traits       = SampleTraits<SAMPLE_FORMAT>;
124    using SampleType   = typename SampleTraits<SAMPLE_FORMAT>::SampleType;
125    using ComputedType = typename SampleTraits<SAMPLE_FORMAT>::ComputedType;
126
127    if ((buffer == nullptr) || (out_packed == nullptr))
128        return ZX_ERR_INVALID_ARGS;
129
130    if (finished())
131        return ZX_ERR_BAD_STATE;
132
133    ZX_DEBUG_ASSERT(frames_produced_ < frames_to_produce_);
134    uint64_t todo = fbl::min<uint64_t>(frames_to_produce_ - frames_produced_,
135                                        buf_space / frame_size_);
136    double pos = sine_scalar_ * static_cast<double>(frames_produced_);
137    auto   buf = reinterpret_cast<SampleType*>(buffer);
138
139    for (uint64_t i = 0; i < todo; ++i) {
140        auto val = static_cast<ComputedType>(amp_ * sin(pos));
141
142        for (uint32_t j = 0; j < channels_; ++j)
143            *(buf++) = Traits::encode(val);
144
145        pos += sine_scalar_;
146    }
147
148    *out_packed = static_cast<uint32_t>(todo * frame_size_);
149    frames_produced_ += todo;
150
151    return ZX_OK;
152}
153