diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8fa63bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +pkg/* +Gemfile.lock diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..fa75df1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec diff --git a/README.md b/README.md new file mode 100644 index 0000000..c05b809 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +[![Version ](https://img.shields.io/gem/v/windsp.svg)](https://rubygems.org/gems/windsp) +WinDSP +====== + +This software is published at https://github.com/unak/windsp . + + +What's This? +------------ + +`/dev/dsp` emulator for Windows. + + +Requirement +----------- + +Ruby 2.0.0 or later. + +And, of cource, Windows :-) + + +How to Use +---------- + +Simply require it. +After requiring, you can write PCM to pseudo I/O via `open "/dev/dsp"`. + + +License +------- + +Copyright (c) 2015 NAKAMURA Usaku usa@garbagecollect.jp + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..cad27d2 --- /dev/null +++ b/Rakefile @@ -0,0 +1,2 @@ +# -*- Ruby -*- +require "bundler/gem_tasks" diff --git a/lib/windsp.rb b/lib/windsp.rb new file mode 100644 index 0000000..ea38aa0 --- /dev/null +++ b/lib/windsp.rb @@ -0,0 +1,98 @@ +require 'fiddle/import' + +class WinDSP + module WinMM + extend Fiddle::Importer + dlload "winmm.dll" + extern "int PlaySound(void *, void *, long)" + + SND_SYNC = 0x0000 + SND_ASYNC = 0x0001 + SND_NODEFAULT = 0x0002 + SND_MEMORY = 0x0004 + SND_LOOP = 0x0008 + SND_NOSTOP = 0x0010 + SND_NOWAIT = 0x00002000 + SND_ALIAS = 0x00010000 + SND_ALIAS_ID = 0x00110000 + SND_FILENAME = 0x00020000 + SND_RESOURCE = 0x00040004 + SND_PURGE = 0x0040 + SND_APPLICATION = 0x0080 + SND_SENTRY = 0x00080000 + SND_RING = 0x00100000 + SND_SYSTEM = 0x00200000 + + def self.make_wave(pcm) + head = ["WAVEfmt ", 16, 1, 1, 8000, 8000 * 1 * 1, 1 * 1, 8].pack('a*VvvVVvv') + data = ["data", pcm.bytesize, pcm].pack('a*Va*') + ["RIFF", head.bytesize + data.bytesize, head, data].pack('a*Va*a*') + end + + def self.play_pcm(pcm, sync: true) + flags = SND_NODEFAULT | SND_MEMORY + flags |= SND_ASYNC unless sync + PlaySound(make_wave(pcm), nil, flags) + end + end + + VERSION = "0.0.1" + + def self.open(*rest, &block) + io = self.new(rest) + if block + begin + block.call(io) + ensure + io.close + end + else + io + end + end + + def initialize(*rest) + @buf = "" + end + + def close + flush + WinMM.PlaySound(nil, nil, 0) + end + + def flush + if @buf.bytesize >= 0 + until WinMM.play_pcm(@buf, sync: false) + sleep 0.1 + end + end + @buf = "" + WinMM.play_pcm("", sync: true) + end + + def write(str) + @buf << str + flush if @buf.bytesize > 8000 * 1 * 1 * 4 + end + + alias binwrite write + alias print write + alias syswrite write + alias << write +end + +module Kernel + private + alias windsp_orig_open open + class << self + alias windsp_orig_open open + end + def open(name, *rest, &block) + if name == "/dev/dsp" + WinDSP.open(*rest, &block) + else + windsp_orig_open(name, *rest, &block) + end + end + module_function :open +end diff --git a/windsp.gemspec b/windsp.gemspec new file mode 100644 index 0000000..0c53592 --- /dev/null +++ b/windsp.gemspec @@ -0,0 +1,22 @@ +# coding: utf-8 +# -*- Ruby -*- +require File.expand_path("lib/windsp") + +Gem::Specification.new do |spec| + spec.name = "windsp" + spec.version = WinDSP::VERSION + spec.authors = ["U.Nakamura"] + spec.email = ["usa@garbagecollect.jp"] + spec.description = %q{`/dev/dsp` emulator for Windows} + spec.summary = %q{`/dev/dsp` emulator for Windows} + spec.homepage = "https://github.com/unak/windsp" + spec.license = "BSD-2-Clause" + + spec.files = `git ls-files`.split($/) + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } + spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.3" + spec.add_development_dependency "rake" +end