diff options
-rw-r--r-- | media/audio/simple_sources.cc | 18 | ||||
-rw-r--r-- | media/audio/simple_sources.h | 1 | ||||
-rw-r--r-- | media/audio/win/audio_output_win_unittest.cc | 59 |
3 files changed, 50 insertions, 28 deletions
diff --git a/media/audio/simple_sources.cc b/media/audio/simple_sources.cc index 4078315..4cd0ba3 100644 --- a/media/audio/simple_sources.cc +++ b/media/audio/simple_sources.cc @@ -21,7 +21,8 @@ SineWaveAudioSource::SineWaveAudioSource(Format format, int channels, : format_(format), channels_(channels), freq_(freq), - sample_freq_(sample_freq) { + sample_freq_(sample_freq), + time_state_(0) { // TODO(cpu): support other formats. DCHECK((format_ == FORMAT_16BIT_LINEAR_PCM) && (channels_ == 1)); } @@ -31,14 +32,19 @@ SineWaveAudioSource::SineWaveAudioSource(Format format, int channels, uint32 SineWaveAudioSource::OnMoreData( AudioOutputStream* stream, uint8* dest, uint32 max_size, AudioBuffersState audio_buffers) { - const double kTwoPi = 2.0 * 3.141592653589; + const double kTwoPi = 2.0 * 3.141592653589793; double f = freq_ / sample_freq_; int16* sin_tbl = reinterpret_cast<int16*>(dest); uint32 len = max_size / sizeof(int16); - // The table is filled with s(t) = 32768*sin(2PI*f*t). - for (uint32 ix = 0; ix != len; ++ix) { - double th = kTwoPi * ix * f; - sin_tbl[ix] = static_cast<int16>((1 << 15) * sin(th)); + + // The table is filled with s(t) = kint16max*sin(Theta*t), + // where Theta = 2*PI*fs. + // We store the discrete time value |t| in a member to ensure that the + // next pass starts at a correct state. + for (uint32 n = 0; n < len; ++n) { + double theta = kTwoPi * f; + sin_tbl[n] = static_cast<int16>(kint16max * sin(theta * time_state_)); + ++time_state_; } return max_size; } diff --git a/media/audio/simple_sources.h b/media/audio/simple_sources.h index dd2782c..a4373a0 100644 --- a/media/audio/simple_sources.h +++ b/media/audio/simple_sources.h @@ -36,6 +36,7 @@ class MEDIA_EXPORT SineWaveAudioSource int channels_; double freq_; double sample_freq_; + int time_state_; }; // Defines an interface for pushing audio output. In contrast, the interfaces diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index 4fcc5c8..d2447c5 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc @@ -10,7 +10,11 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/sync_socket.h" +#include "base/win/scoped_com_initializer.h" +#include "base/win/windows_version.h" +#include "media/base/limits.h" #include "media/audio/audio_io.h" +#include "media/audio/audio_util.h" #include "media/audio/audio_manager.h" #include "media/audio/simple_sources.h" #include "testing/gmock/include/gmock/gmock.h" @@ -25,6 +29,8 @@ using ::testing::NiceMock; using ::testing::NotNull; using ::testing::Return; +using base::win::ScopedCOMInitializer; + static const wchar_t kAudioFile1_16b_m_16K[] = L"media\\test\\data\\sweep02_16b_mono_16KHz.raw"; @@ -231,7 +237,7 @@ TEST(WinAudioTest, PCMWaveStreamGetAndClose) { oas->Close(); } -// Test that can it be cannot be created with crazy parameters +// Test that can it be cannot be created with invalid parameters. TEST(WinAudioTest, SanityOnMakeParams) { if (IsRunningHeadless()) return; @@ -255,7 +261,8 @@ TEST(WinAudioTest, SanityOnMakeParams) { EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, 0))); EXPECT_TRUE(NULL == audio_man->MakeAudioOutputStream( - AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, 100000))); + AudioParameters(fmt, CHANNEL_LAYOUT_MONO, 8000, 16, + media::Limits::kMaxSamplesPerPacket + 1))); } // Test that it can be opened and closed. @@ -533,11 +540,10 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { oas->Close(); } -// With the low latency mode, we have two buffers instead of 3 and we -// should be able to handle 20ms buffers at 44KHz. See also the SyncSocketBasic -// test below. -// TODO(cpu): right now the best we can do is 50ms before it sounds choppy. -TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44KssLowLatency) { +// With the low latency mode, WASAPI is utilized by default for Vista and +// higher and Wave is used for XP and lower. It is possible to utilize a +// smaller buffer size for WASAPI than for Wave. +TEST(WinAudioTest, PCMWaveStreamPlay200HzToneLowLatency) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); @@ -545,15 +551,23 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44KssLowLatency) { if (!audio_man->HasAudioOutputDevices()) return; - uint32 samples_50_ms = AudioParameters::kAudioCDSampleRate / 20; + // The WASAPI API requires a correct COM environment. + ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); + + // Use 10 ms buffer size for WASAPI and 50 ms buffer size for Wave. + // Take the existing native sample rate into account. + int sample_rate = static_cast<int>(media::GetAudioHardwareSampleRate()); + uint32 samples_10_ms = sample_rate / 100; + int n = 1; + (base::win::GetVersion() <= base::win::VERSION_XP) ? n = 5 : n = 1; AudioOutputStream* oas = audio_man->MakeAudioOutputStream( AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, - CHANNEL_LAYOUT_MONO, AudioParameters::kAudioCDSampleRate, - 16, samples_50_ms)); + CHANNEL_LAYOUT_MONO, sample_rate, + 16, n * samples_10_ms)); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, - 200.0, AudioParameters::kAudioCDSampleRate); + 200.0, sample_rate); EXPECT_TRUE(oas->Open()); oas->SetVolume(1.0); @@ -651,7 +665,7 @@ struct SyncThreadContext { base::SyncSocket* socket; int sample_rate; double sine_freq; - uint32 packet_size; + uint32 packet_size_bytes; }; // This thread provides the data that the SyncSocketSource above needs @@ -672,11 +686,11 @@ DWORD __stdcall SyncSocketThread(void* context) { AudioBuffersState buffers_state; int times = 0; - for (int ix = 0; ix < kTwoSecBytes; ix += ctx.packet_size) { + for (int ix = 0; ix < kTwoSecBytes; ix += ctx.packet_size_bytes) { if (ctx.socket->Receive(&buffers_state, sizeof(buffers_state)) == 0) break; if ((times > 0) && (buffers_state.pending_bytes < 1000)) __debugbreak(); - ctx.socket->Send(&buffer[ix], ctx.packet_size); + ctx.socket->Send(&buffer[ix], ctx.packet_size_bytes); ++times; } @@ -685,11 +699,13 @@ DWORD __stdcall SyncSocketThread(void* context) { } // Test the basic operation of AudioOutputStream used with a SyncSocket. -// The emphasis is to test low-latency with buffers less than 100ms. With -// the waveout api it seems not possible to go below 50ms. In this test -// you should hear a continous 200Hz tone. -// -// TODO(cpu): This actually sounds choppy most of the time. Fix it. +// The emphasis is to verify that it is possible to feed data to the audio +// layer using a source based on SyncSocket. In a real situation we would +// go for the low-latency version in combination with SyncSocket, but to keep +// the test more simple, AUDIO_PCM_LINEAR is utilized instead. The main +// principle of the test still remains and we avoid the additional complexity +// related to the two different audio-layers for AUDIO_PCM_LOW_LATENCY. +// In this test you should hear a continuous 200Hz tone for 2 seconds. TEST(WinAudioTest, SyncSocketBasic) { if (IsRunningHeadless()) return; @@ -702,11 +718,10 @@ TEST(WinAudioTest, SyncSocketBasic) { int sample_rate = AudioParameters::kAudioCDSampleRate; const uint32 kSamples20ms = sample_rate / 50; AudioOutputStream* oas = audio_man->MakeAudioOutputStream( - AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, + AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO, sample_rate, 16, kSamples20ms)); ASSERT_TRUE(NULL != oas); - // compute buffer size for 20ms of audio, 882 samples (mono). ASSERT_TRUE(oas->Open()); base::SyncSocket* sockets[2]; @@ -717,7 +732,7 @@ TEST(WinAudioTest, SyncSocketBasic) { SyncThreadContext thread_context; thread_context.sample_rate = sample_rate; thread_context.sine_freq = 200.0; - thread_context.packet_size = kSamples20ms; + thread_context.packet_size_bytes = kSamples20ms * 2; thread_context.socket = sockets[1]; HANDLE thread = ::CreateThread(NULL, 0, SyncSocketThread, |