forked from microsoft/Xbox-ATG-Samples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathToneSampleGenerator.cpp
177 lines (150 loc) · 4.82 KB
/
ToneSampleGenerator.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//--------------------------------------------------------------------------------------
//
// Advanced Technology Group (ATG)
// Copyright (C) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
//
// ToneSampleGenerator.h
//
#include "pch.h"
#include "ToneSampleGenerator.h"
const int TONE_DURATION_SEC = 30;
const double TONE_AMPLITUDE = 0.5; // Scalar value, should be between 0.0 - 1.0
//
// Convert from double to float, byte, short or int32.
//
template<>
float Convert<float>(double Value)
{
return static_cast<float>(Value);
};
template<>
short Convert<short>(double Value)
{
return static_cast<short>(Value * _I16_MAX);
};
//
// ToneSampleGenerator()
//
ToneSampleGenerator::ToneSampleGenerator()
{
m_SampleQueue = nullptr;
m_SampleQueueTail = &m_SampleQueue;
}
//
// ~ToneSampleGenerator()
//
ToneSampleGenerator::~ToneSampleGenerator()
{
// Flush unused samples
Flush();
}
//
// GenerateSampleBuffer()
//
// Create a linked list of sample buffers
//
HRESULT ToneSampleGenerator::GenerateSampleBuffer(unsigned long Frequency, unsigned int FramesPerPeriod, WAVEFORMATEX *wfx )
{
HRESULT hr = S_OK;
uint32_t renderBufferSizeInBytes = FramesPerPeriod * wfx->nBlockAlign;
uint64_t renderDataLength = ( wfx->nSamplesPerSec * TONE_DURATION_SEC * wfx->nBlockAlign ) + ( renderBufferSizeInBytes - 1 );
uint64_t renderBufferCount = renderDataLength / renderBufferSizeInBytes;
double theta = 0;
for( UINT64 i = 0; i < renderBufferCount; i++ )
{
RenderBuffer *SampleBuffer = new (std::nothrow) RenderBuffer();
if (nullptr == SampleBuffer)
{
return E_OUTOFMEMORY;
}
SampleBuffer->BufferSize = renderBufferSizeInBytes;
SampleBuffer->BytesFilled = renderBufferSizeInBytes;
SampleBuffer->Buffer = new (std::nothrow) unsigned char[ renderBufferSizeInBytes ];
if (nullptr == SampleBuffer->Buffer)
{
return E_OUTOFMEMORY;
}
switch( CalculateMixFormatType( wfx ) )
{
case RenderSampleType::SampleType16BitPCM:
GenerateSineSamples<short>( SampleBuffer->Buffer, SampleBuffer->BufferSize, Frequency, wfx->nChannels, wfx->nSamplesPerSec, TONE_AMPLITUDE, &theta);
break;
case RenderSampleType::SampleTypeFloat:
GenerateSineSamples<float>( SampleBuffer->Buffer, SampleBuffer->BufferSize, Frequency, wfx->nChannels, wfx->nSamplesPerSec, TONE_AMPLITUDE, &theta);
break;
default:
return E_UNEXPECTED;
break;
}
*m_SampleQueueTail = SampleBuffer;
m_SampleQueueTail = &SampleBuffer->Next;
}
return hr;
}
//
// GenerateSineSamples()
//
// Generate samples which represent a sine wave that fits into the specified buffer.
//
// T: Type of data holding the sample (short, int, byte, float)
// Buffer - Buffer to hold the samples
// BufferLength - Length of the buffer.
// ChannelCount - Number of channels per audio frame.
// SamplesPerSecond - Samples/Second for the output data.
// InitialTheta - Initial theta value - start at 0, modified in this function.
//
template <typename T>
void ToneSampleGenerator::GenerateSineSamples(unsigned char *Buffer, size_t BufferLength, unsigned long Frequency, unsigned short ChannelCount, unsigned long SamplesPerSecond, double Amplitude, double *InitialTheta)
{
double sampleIncrement = (Frequency * (M_PI*2)) / (double)SamplesPerSecond;
T *dataBuffer = reinterpret_cast<T *>(Buffer);
double theta = (InitialTheta != NULL ? *InitialTheta : 0);
for (size_t i = 0 ; i < BufferLength / sizeof(T) ; i += ChannelCount)
{
double sinValue = Amplitude * sin( theta );
for(size_t j = 0 ;j < ChannelCount; j++)
{
dataBuffer[i+j] = Convert<T>(sinValue);
}
theta += sampleIncrement;
}
if (InitialTheta != NULL)
{
*InitialTheta = theta;
}
}
//
// FillSampleBuffer()
//
// File the Data buffer of size BytesToRead with the first item in the queue. Caller is responsible for allocating and freeing buffer
//
HRESULT ToneSampleGenerator::FillSampleBuffer( unsigned int BytesToRead, unsigned char *Data )
{
if (nullptr == Data)
{
return E_POINTER;
}
RenderBuffer *SampleBuffer = m_SampleQueue;
if (BytesToRead > SampleBuffer->BufferSize)
{
return E_INVALIDARG;
}
CopyMemory( Data, SampleBuffer->Buffer, BytesToRead );
m_SampleQueue = m_SampleQueue->Next;
return S_OK;
}
//
// Flush()
//
// Remove and free unused samples from the queue
//
void ToneSampleGenerator::Flush()
{
while( m_SampleQueue != nullptr )
{
RenderBuffer *SampleBuffer = m_SampleQueue;
m_SampleQueue = SampleBuffer->Next;
SAFE_DELETE( SampleBuffer );
}
}