summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorcpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-23 01:27:49 +0000
committercpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-23 01:27:49 +0000
commit936f535cfbf9d82559cd54e1011f54480839b790 (patch)
tree8727353fafe26f937d8ae69c889015b71671ed2f /media
parent920603a234542c6fa484bb00ae2e8e20e7ffabbc (diff)
downloadchromium_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.cc71
-rw-r--r--media/audio/simple_sources.h65
-rw-r--r--media/audio/win/audio_output_win_unittest.cc111
-rw-r--r--media/test/data/sweep02_16b_mono_16KHz.rawbin0 -> 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
new file mode 100644
index 0000000..5dd4dc4
--- /dev/null
+++ b/media/test/data/sweep02_16b_mono_16KHz.raw
Binary files differ