1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/midi/usb_midi_input_stream.h"
6
7#include <string.h>
8#include <map>
9#include <vector>
10
11#include "base/logging.h"
12#include "media/midi/usb_midi_device.h"
13#include "media/midi/usb_midi_jack.h"
14
15namespace media {
16
17UsbMidiInputStream::JackUniqueKey::JackUniqueKey(UsbMidiDevice* device,
18                                                 int endpoint_number,
19                                                 int cable_number)
20    : device(device),
21      endpoint_number(endpoint_number),
22      cable_number(cable_number) {}
23
24bool UsbMidiInputStream::JackUniqueKey::operator==(
25    const JackUniqueKey& that) const {
26  return device == that.device &&
27      endpoint_number == that.endpoint_number &&
28      cable_number == that.cable_number;
29}
30
31bool UsbMidiInputStream::JackUniqueKey::operator<(
32    const JackUniqueKey& that) const {
33  if (device != that.device)
34    return device < that.device;
35  if (endpoint_number != that.endpoint_number)
36    return endpoint_number < that.endpoint_number;
37  return cable_number < that.cable_number;
38}
39
40UsbMidiInputStream::UsbMidiInputStream(const std::vector<UsbMidiJack>& jacks,
41                                       Delegate* delegate)
42    : delegate_(delegate) {
43  for (size_t i = 0; i < jacks.size(); ++i) {
44    jack_dictionary_.insert(
45        std::make_pair(JackUniqueKey(jacks[i].device,
46                                     jacks[i].endpoint_number(),
47                                     jacks[i].cable_number),
48                       i));
49  }
50}
51
52UsbMidiInputStream::~UsbMidiInputStream() {}
53
54void UsbMidiInputStream::OnReceivedData(UsbMidiDevice* device,
55                                        int endpoint_number,
56                                        const uint8* data,
57                                        size_t size,
58                                        base::TimeTicks time) {
59  DCHECK_EQ(0u, size % kPacketSize);
60  size_t current = 0;
61  while (current + kPacketSize <= size) {
62    ProcessOnePacket(device, endpoint_number, &data[current], time);
63    current += kPacketSize;
64  }
65}
66
67void UsbMidiInputStream::ProcessOnePacket(UsbMidiDevice* device,
68                                          int endpoint_number,
69                                          const uint8* packet,
70                                          base::TimeTicks time) {
71  // The first 4 bytes of the packet is accessible here.
72  uint8 code_index = packet[0] & 0x0f;
73  uint8 cable_number = packet[0] >> 4;
74  const size_t packet_size_table[16] = {
75    0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1,
76  };
77  size_t packet_size = packet_size_table[code_index];
78  if (packet_size == 0) {
79    // These CINs are reserved. Ignore them.
80    DVLOG(1) << "code index number (" << code_index << ") arrives "
81             << "but it is reserved.";
82    return;
83  }
84  std::map<JackUniqueKey, size_t>::const_iterator it =
85      jack_dictionary_.find(JackUniqueKey(device,
86                                          endpoint_number,
87                                          cable_number));
88  if (it != jack_dictionary_.end())
89    delegate_->OnReceivedData(it->second, &packet[1], packet_size, time);
90}
91
92std::vector<UsbMidiInputStream::JackUniqueKey>
93UsbMidiInputStream::RegisteredJackKeysForTesting() const {
94  std::vector<JackUniqueKey> result(jack_dictionary_.size(),
95                                    JackUniqueKey(0, 0, 0));
96  for (std::map<JackUniqueKey, size_t>::const_iterator it =
97           jack_dictionary_.begin();
98       it != jack_dictionary_.end(); ++it) {
99    DCHECK_LT(it->second, result.size());
100    result[it->second] = it->first;
101  }
102  return result;
103}
104
105}  // namespace media
106