diff options
author | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-23 01:27:49 +0000 |
---|---|---|
committer | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-23 01:27:49 +0000 |
commit | 936f535cfbf9d82559cd54e1011f54480839b790 (patch) | |
tree | 8727353fafe26f937d8ae69c889015b71671ed2f /media | |
parent | 920603a234542c6fa484bb00ae2e8e20e7ffabbc (diff) | |
download | chromium_src-936f535cfbf9d82559cd54e1011f54480839b790.zip chromium_src-936f535cfbf9d82559cd54e1011f54480839b790.tar.gz chromium_src-936f535cfbf9d82559cd54e1011f54480839b790.tar.bz2 |
Non blocking audio source
- New interface PushAudioOutput for push model source
- New adapter class PushSource which converts from pull model to push model
- A test audio file (made by me using freeware program WavePad)
- A new test
TEST=unit test included.
Review URL: http://codereview.chromium.org/115223
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16825 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/simple_sources.cc | 71 | ||||
-rw-r--r-- | media/audio/simple_sources.h | 65 | ||||
-rw-r--r-- | media/audio/win/audio_output_win_unittest.cc | 111 | ||||
-rw-r--r-- | media/test/data/sweep02_16b_mono_16KHz.raw | bin | 0 -> 63988 bytes |
4 files changed, 241 insertions, 6 deletions
diff --git a/media/audio/simple_sources.cc b/media/audio/simple_sources.cc index b6fda23..6643fc1 100644 --- a/media/audio/simple_sources.cc +++ b/media/audio/simple_sources.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <algorithm> #include <math.h> #include "base/basictypes.h" @@ -9,6 +10,9 @@ #include "media/audio/audio_output.h" #include "media/audio/simple_sources.h" +////////////////////////////////////////////////////////////////////////////// +// SineWaveAudioSource implementation. + SineWaveAudioSource::SineWaveAudioSource(Format format, int channels, double freq, double sample_freq) : format_(format), @@ -41,3 +45,70 @@ void SineWaveAudioSource::OnClose(AudioOutputStream* stream) { void SineWaveAudioSource::OnError(AudioOutputStream* stream, int code) { NOTREACHED(); } + +////////////////////////////////////////////////////////////////////////////// +// PushSource implementation. + +PushSource::PushSource(size_t packet_size) + : packet_size_(packet_size), buffered_bytes_(0) { +} + +PushSource::~PushSource() { + CleanUp(); +} + +size_t PushSource::OnMoreData(AudioOutputStream* stream, + void* dest, size_t max_size) { + Packet packet; + { + AutoLock auto_lock(lock_); + // Under lock processing in this scope. + if (!packets_.size()) + return 0; + packet = packets_.front(); + packets_.pop_front(); + buffered_bytes_ -= packet.size; + } + size_t size = std::min(max_size, packet.size); + memcpy(dest, packet.buffer, size); + delete [] packet.buffer; + return size; +} + +void PushSource::OnClose(AudioOutputStream* stream) { + CleanUp(); +} + +void PushSource::OnError(AudioOutputStream* stream, int code) { + NOTREACHED(); +} + +// TODO(cpu): Manage arbitrary large sizes. +bool PushSource::Write(const void *data, size_t len) { + if ((len == 0) || (len > packet_size_)) { + NOTREACHED(); + return false; + } + Packet packet = { new char[len], len }; + memcpy(packet.buffer, data, packet.size); + // Under lock processing here. + AutoLock auto_lock(lock_); + packets_.push_back(packet); + buffered_bytes_ += len; + return true; +} + +size_t PushSource::UnProcessedBytes() { + AutoLock auto_lock(lock_); + return buffered_bytes_; +} + +void PushSource::CleanUp() { + AutoLock auto_lock(lock_); + PacketList::const_iterator it; + for (it = packets_.begin(); it != packets_.end(); ++it) { + delete it->buffer; + buffered_bytes_ -= it->size; + } + packets_.clear(); +} diff --git a/media/audio/simple_sources.h b/media/audio/simple_sources.h index 992aba5..1da4d4f 100644 --- a/media/audio/simple_sources.h +++ b/media/audio/simple_sources.h @@ -5,18 +5,18 @@ #ifndef MEDIA_AUDIO_SIMPLE_SOURCES_H_ #define MEDIA_AUDIO_SIMPLE_SOURCES_H_ +#include <list> + +#include "base/lock.h" #include "media/audio/audio_output.h" -// An audio source that produces a pure sinusoidal tone. Each platform needs -// a slightly different implementation because it needs to handle the native -// audio buffer format. +// An audio source that produces a pure sinusoidal tone. class SineWaveAudioSource : public AudioOutputStream::AudioSourceCallback { public: enum Format { FORMAT_8BIT_LINEAR_PCM, FORMAT_16BIT_LINEAR_PCM, }; - // |channels| is the number of audio channels, |freq| is the frequency in // hertz and it has to be less than half of the sampling frequency // |sample_freq| or else you will get aliasing. @@ -37,4 +37,61 @@ class SineWaveAudioSource : public AudioOutputStream::AudioSourceCallback { double sample_freq_; }; +// Defines an interface for pushing audio output. In contrast, the interfaces +// defined by AudioSourceCallback are pull model only. +class PushAudioOutput { + public: + virtual ~PushAudioOutput(){} + + // Write audio data to the audio device. It will be played eventually. + // Returns false on failure. + virtual bool Write(const void* data, size_t len) = 0; + + // Returns the number of bytes that have been buffered but not yet given + // to the audio device. + virtual size_t UnProcessedBytes() = 0; +}; + +// A fairly basic class to connect a push model provider PushAudioOutput to +// a pull model provider AudioSourceCallback. Fundamentally it manages a series +// of audio buffers and is unaware of the actual audio format. +class PushSource : public AudioOutputStream::AudioSourceCallback, + public PushAudioOutput { + public: + // Construct the audio source. Pass the same |packet_size| specified in the + // AudioOutputStream::Open() here. + explicit PushSource(size_t packet_size); + virtual ~PushSource(); + + // Write one buffer. The ideal size is |packet_size| but smaller sizes are + // accepted. Bigger sizes are an error. Returns false on error. + virtual bool Write(const void* data, size_t len); + + // Return the total number of bytes not given to the audio device yet. + virtual size_t UnProcessedBytes(); + + // Implementation of AudioSourceCallback. + virtual size_t OnMoreData(AudioOutputStream* stream, + void* dest, size_t max_size); + virtual void OnClose(AudioOutputStream* stream); + virtual void OnError(AudioOutputStream* stream, int code); + + private: + // Defines the unit of playback. We own the memory pointed by |buffer|. + struct Packet { + char* buffer; + size_t size; + }; + + // Free acquired resources. + void CleanUp(); + + const size_t packet_size_; + typedef std::list<Packet> PacketList; + PacketList packets_; + size_t buffered_bytes_; + // Serialize access to packets_ and buffered_bytes_ using this lock. + Lock lock_; +}; + #endif // MEDIA_AUDIO_SIMPLE_SOURCES_H_ diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index d9ed2f9..5366754 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc @@ -5,12 +5,17 @@ #include <windows.h> #include "base/basictypes.h" +#include "base/base_paths.h" +#include "base/file_util.h" #include "media/audio/audio_output.h" #include "media/audio/simple_sources.h" #include "testing/gtest/include/gtest/gtest.h" namespace { +const wchar_t kAudioFile1_16b_m_16K[] + = L"media\\test\\data\\sweep02_16b_mono_16KHz.raw"; + // This class allows to find out if the callbacks are occurring as // expected and if any error has been reported. class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { @@ -105,7 +110,7 @@ class TestSourceDoubleBuffer : public TestSourceBasic { // in the OnMoreData callback. class TestSourceLaggy : public TestSourceBasic { public: - TestSourceLaggy(int laggy_after_buffer, int lag_in_ms) + TestSourceLaggy(int laggy_after_buffer, int lag_in_ms) : laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) { } virtual size_t OnMoreData(AudioOutputStream* stream, @@ -122,6 +127,53 @@ class TestSourceLaggy : public TestSourceBasic { int lag_in_ms_; }; +// Helper class to memory map an entire file. The mapping is read-only. Don't +// use for gigabyte-sized files. Attempts to write to this memory generate +// memory access violations. +class ReadOnlyMappedFile { + public: + ReadOnlyMappedFile(const wchar_t* file_name) + : fmap_(NULL), start_(NULL), size_(0) { + HANDLE file = ::CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == file) + return; + fmap_ = ::CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL); + ::CloseHandle(file); + if (!fmap_) + return; + start_ = reinterpret_cast<char*>(::MapViewOfFile(fmap_, FILE_MAP_READ, + 0, 0, 0)); + if (!start_) + return; + MEMORY_BASIC_INFORMATION mbi = {0}; + ::VirtualQuery(start_, &mbi, sizeof(mbi)); + size_ = mbi.RegionSize; + } + ~ReadOnlyMappedFile() { + if (start_) { + ::UnmapViewOfFile(start_); + ::CloseHandle(fmap_); + } + } + // Returns true if the file was successfully mapped. + bool is_valid() const { + return ((start_ > 0) && (size_ > 0)); + } + // Returns the size in bytes of the mapped memory. + size_t size() const { + return size_; + } + // Returns the memory backing the file. + const void* GetChunkAt(size_t offset) { + return &start_[offset]; + } + + private: + HANDLE fmap_; + char* start_; + size_t size_; +}; // ============================================================================ // Validate that the AudioManager::AUDIO_MOCK callbacks work. @@ -259,7 +311,7 @@ TEST(WinAudioTest, PCMWaveSlowSource) { ASSERT_TRUE(NULL != oas); TestSourceLaggy test_laggy(2, 90); EXPECT_TRUE(oas->Open(512)); - // The test parameters cause a callback every 32 ms and the source is + // The test parameters cause a callback every 32 ms and the source is // sleeping for 90 ms, so it is guaranteed that we run out of ready buffers. oas->Start(&test_laggy); ::Sleep(1000); @@ -335,3 +387,58 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { oas->Close(); } +// Uses the PushSource to play a 2 seconds file clip for about 5 seconds. We +// try hard to generate situation where the two threads are accessing the +// object roughly at the same time. What you hear is a sweeping tone from 1KHz +// to 2KHz with a bit of fade out at the end for one second. The file is two +// of these sweeping tones back to back. +TEST(WinAudioTest, PushSourceFile16KHz) { + if (IsRunningHeadless()) + return; + // Open sweep02_16b_mono_16KHz.raw which has no format. It contains the + // raw 16 bit samples for a single channel in little-endian format. The + // creation sample rate is 16KHz. + FilePath audio_file; + ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &audio_file)); + audio_file = audio_file.Append(kAudioFile1_16b_m_16K); + // Map the entire file in memory. + ReadOnlyMappedFile file_reader(audio_file.value().c_str()); + ASSERT_TRUE(file_reader.is_valid()); + + AudioManager* audio_man = AudioManager::GetAudioManager(); + ASSERT_TRUE(NULL != audio_man); + if (!audio_man->HasAudioDevices()) + return; + AudioOutputStream* oas = + audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, 16000, 16); + ASSERT_TRUE(NULL != oas); + + // compute buffer size for 100ms of audio. Which is 3200 bytes. + const size_t kSize50ms = 2 * (16000 / 1000) * 100; + EXPECT_TRUE(oas->Open(kSize50ms)); + + size_t offset = 0; + const size_t kMaxStartOffset = file_reader.size() - kSize50ms; + + // We buffer and play at the same time, buffering happens every ~10ms and the + // consuming of the buffer happens every ~50ms. We do 100 buffers which + // effectively wrap around the file more than once. + PushSource push_source(kSize50ms); + for (size_t ix = 0; ix != 100; ++ix) { + push_source.Write(file_reader.GetChunkAt(offset), kSize50ms); + if (ix == 2) { + // For glitch free, start playing after some buffers are in. + oas->Start(&push_source); + } + ::Sleep(10); + offset += kSize50ms; + if (offset > kMaxStartOffset) + offset = 0; + } + + // Play a little bit more of the file. + ::Sleep(4000); + + oas->Stop(); + oas->Close(); +} diff --git a/media/test/data/sweep02_16b_mono_16KHz.raw b/media/test/data/sweep02_16b_mono_16KHz.raw Binary files differnew file mode 100644 index 0000000..5dd4dc4 --- /dev/null +++ b/media/test/data/sweep02_16b_mono_16KHz.raw |