File: websocket_server.rb

package info (click to toggle)
sonic-pi 3.2.2~repack-8
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 71,872 kB
  • sloc: ruby: 30,548; cpp: 8,490; sh: 957; ansic: 461; erlang: 360; lisp: 141; makefile: 44
file content (119 lines) | stat: -rw-r--r-- 3,155 bytes parent folder | download | duplicates (4)
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
#--
# This file is part of Sonic Pi: http://sonic-pi.net
# Full project source: https://github.com/samaaron/sonic-pi
# License: https://github.com/samaaron/sonic-pi/blob/master/LICENSE.md
#
# Copyright 2018 by Sam Aaron (http://sam.aaron.name).
# All rights reserved.
#
# Permission is granted for use, copying, modification, and
# distribution of modified versions of this work as long as this
# notice is included.
#++

require 'socket'
require_relative "../util"
require 'rubame'

# monkey patch rubame to add the ability to send binary
# data down the websocket
module Rubame
  class Client
    def __sonic_pi_send_binary(data)
      frame = WebSocket::Frame::Outgoing::Server.new(:version => @handshake.version, :data => data, :type => :binary)
      begin
        @socket.write frame
        @socket.flush
      rescue
        @server.close(self) unless @closed
      end
    end
  end
end

module SonicPi
  module OSC
    class WebSocketServer
      include Util

      def initialize(port, opts={})
        @port = port
        @opts = opts
        @server = Rubame::Server.new("localhost", port)
        @client = nil
        @matchers = {}
        @decoder = FastOsc
        @encoder = FastOsc
        @listener_thread = Thread.new {start_listener}
        @connected = false
      end

      def send(pattern, *args)
        log "websocket outgoing: #{pattern}"
        msg = @encoder.encode_single_message(pattern, args)

        # TODO - this is just temporary to stop the initial messages
        # falling into the ether before a client has connected.
        # Need to properly consider this behaviour and appropriately
        # improve.
        while ! @client
          Kernel.sleep 0.1
        end
        @client.__sonic_pi_send_binary(msg)
      end

      def send_ts(ts, pattern, *args)
        msg = @encoder.encode_single_bundle(ts, pattern, args)
        @client.__sonic_pi_send_binary(msg)
      end

      def add_method(address_pattern, &proc)
        @matchers[address_pattern] = proc
      end

      def to_s
        "#<SonicPi::OSC::WebSocketServer port: #{@port}, opts: #{@opts.inspect}>"
      end

      def stop
        @listener_thread.kill
        @socket.close
      end

      def inspect
        to_s
      end

      private

      def start_listener
        Kernel.loop do
          @server.run do |client|

            @client = client
            client.onopen do
              log "WebSocketServer reports:  client open #{client}"
            end

            client.onmessage do |mess|
              begin
              o = @decoder.decode_single_message(mess)
                log "websocket incoming: #{o[0]}, #{o[1]}"
              p = @matchers[o[0]]
              p.call(o[1]) if p
              rescue Exception => e
                STDERR.puts "Websocket-based OSC handler exception for address: #{address}"
                STDERR.puts e.message
                STDERR.puts e.backtrace.inspect
              end
            end

            client.onclose do
              log "WebSocketServer reports:  client closed  #{client}"
            end
          end
        end
      end
    end
  end
end