this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat!: Move MIDI-related code into the midi module; Rename root module to midi-synth #1

open opened by danikvitek.eurosky.social targeting main from synth
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:bsvbcgos44pye2a3fz4iwovu/sh.tangled.repo.pull/3ml74t37y2d22
+491 -490
Diff #2
+3 -3
build.zig
··· 28 28 // to our consumers. We must give it a name because a Zig package can expose 29 29 // multiple modules and consumers will need to be able to specify which 30 30 // module they want to access. 31 - const mod = b.addModule("midi", .{ 31 + const mod = b.addModule("midi-synth", .{ 32 32 // The root source file is the "entry point" of this module. Users of 33 33 // this module will only be able to access public declarations contained 34 34 // in this file, which means that if you have declarations that you ··· 58 58 // If neither case applies to you, feel free to delete the declaration you 59 59 // don't need and to put everything under a single module. 60 60 const exe = b.addExecutable(.{ 61 - .name = "midi", 61 + .name = "midi-synth", 62 62 .root_module = b.createModule(.{ 63 63 // b.createModule defines a new module just like b.addModule but, 64 64 // unlike b.addModule, it does not expose the module to consumers of ··· 78 78 // repeated because you are allowed to rename your imports, which 79 79 // can be extremely useful in case of collisions (which can happen 80 80 // importing modules from different packages). 81 - .{ .name = "midi", .module = mod }, 81 + .{ .name = "midi-synth", .module = mod }, 82 82 }, 83 83 }), 84 84 });
+1 -1
src/main.zig
··· 2 2 const Io = std.Io; 3 3 const Allocator = std.mem.Allocator; 4 4 5 - const midi = @import("midi"); 5 + const midi = @import("midi-synth").midi; 6 6 const MidiMessage = midi.protocol.MidiMessage; 7 7 8 8 pub const std_options: std.Options = .{
+486
src/midi.zig
··· 1 + const std = @import("std"); 2 + const Io = std.Io; 3 + const Allocator = std.mem.Allocator; 4 + 5 + pub const protocol = struct { 6 + const unpacked = @import("midi/protocol/unpacked.zig"); 7 + const @"packed" = @import("midi/protocol/packed.zig"); 8 + 9 + pub const MidiMessage = unpacked.MidiMessage; 10 + pub const Status = unpacked.Status; 11 + pub const StatusPayload = unpacked.StatusPayload; 12 + pub const SystemMessage = unpacked.SystemMessage; 13 + pub const SmpteFrame = unpacked.SmpteFrame; 14 + pub const MtcQuarterFrame = unpacked.MtcQuarterFrame; 15 + pub const Framerate = @import("midi/protocol.zig").Framerate; 16 + pub const MtcPieceType = @import("midi/protocol.zig").MtcPieceType; 17 + }; 18 + 19 + pub fn streamMidi( 20 + io: Io, 21 + gpa: Allocator, 22 + midi_dev_reader: *Io.Reader, 23 + out_queue: *Io.Queue(protocol.MidiMessage), 24 + ) (Allocator.Error || Io.Reader.Error || Io.Cancelable)!void { 25 + const MidiByte = protocol.@"packed".MidiByte; 26 + 27 + var state: StreamState = .idle; 28 + defer state.deinit(gpa); 29 + 30 + var mtc_frame_progress: MtcFrameProgress = .none; 31 + 32 + while (true) { 33 + const byte: MidiByte = @bitCast(try midi_dev_reader.takeByte()); 34 + switch (byte.kind) { 35 + .status => switch (handleStatusByte(io, gpa, &state, byte.payload.status, out_queue)) { 36 + .@"continue" => continue, 37 + .@"break" => break, 38 + .@"return" => |res| return res, 39 + }, 40 + .data => switch (handleDataByte(io, gpa, &state, &mtc_frame_progress, @bitCast(byte.payload), out_queue)) { 41 + .@"continue" => continue, 42 + .@"break" => break, 43 + .@"return" => |res| return res, 44 + }, 45 + } 46 + } 47 + } 48 + 49 + fn ControlFlow(comptime C: type, comptime B: type, comptime R: type) type { 50 + return union(enum) { 51 + @"continue": C, 52 + @"break": B, 53 + @"return": R, 54 + }; 55 + } 56 + 57 + inline fn handleStatusByte( 58 + io: Io, 59 + gpa: Allocator, 60 + state: *StreamState, 61 + status: protocol.@"packed".StatusByte, 62 + out_queue: *Io.Queue(protocol.MidiMessage), 63 + ) ControlFlow(void, void, (Io.Cancelable || Allocator.Error)!void) { 64 + switch (status.message) { 65 + .system_message => switch (status.payload.system) { 66 + .system_exclusive => state.replace(gpa, .{ .system_exclusive = .empty }), 67 + inline .mtc_quarter_frame, 68 + .song_position_pointer, 69 + .song_select, 70 + => |tag| state.replace(gpa, @unionInit(StreamState, @tagName(tag), {})), 71 + .end_of_exclusive => { 72 + const message = switch (state.*) { 73 + .system_exclusive => |*array_list| array_list.toOwnedSlice(gpa) catch |err| return .{ .@"return" = err }, 74 + else => { // missed the start of the message, so discard 75 + // std.debug.print("end of exclusive:\n", .{}); 76 + return .@"continue"; 77 + }, 78 + }; 79 + errdefer gpa.free(message); 80 + 81 + state.* = .idle; 82 + out_queue.putOne( 83 + io, 84 + .{ .system = .{ .system_exclusive = message } }, 85 + ) catch |err| switch (err) { 86 + error.Canceled => return .{ .@"return" = error.Canceled }, 87 + error.Closed => return .@"break", 88 + }; 89 + }, 90 + 91 + inline .tune_request, 92 + // real-time 93 + .timing_clock, 94 + .start, 95 + .@"continue", 96 + .stop, 97 + .active_sensing, 98 + .system_reset, 99 + => |tag| out_queue.putOne(io, .{ .system = @unionInit( 100 + protocol.SystemMessage, 101 + @tagName(tag), 102 + {}, 103 + ) }) catch |err| switch (err) { 104 + error.Canceled => return .{ .@"return" = error.Canceled }, 105 + error.Closed => return .@"break", 106 + }, 107 + }, 108 + inline .note_off, 109 + .note_on, 110 + .polyphonic_aftertouch, 111 + .control_change, 112 + .program_change, 113 + .channel_pressure, 114 + .pitch_bend, 115 + => |tag| state.replace(gpa, @unionInit( 116 + StreamState, 117 + @tagName(tag), 118 + .{ .channel = status.payload.channel }, 119 + )), 120 + } 121 + return .@"continue"; 122 + } 123 + 124 + inline fn handleDataByte( 125 + io: Io, 126 + gpa: Allocator, 127 + state: *StreamState, 128 + mtc_frame_progress: *MtcFrameProgress, 129 + payload: packed union(u7) { 130 + data: u7, 131 + mtc_quarter_frame: protocol.@"packed".MtcQuarterFrame, 132 + }, 133 + out_queue: *Io.Queue(protocol.MidiMessage), 134 + ) ControlFlow(void, void, (Io.Cancelable || Allocator.Error)!void) { 135 + switch (state.*) { 136 + .idle => { // missed the start of the message, so no clue where to put the data 137 + // std.debug.print("data byte when idle: {x}\n", .{byte.payload.data}); 138 + }, 139 + .system_exclusive => |*message| { 140 + message.append(gpa, payload.data) catch |err| return .{ .@"return" = err }; 141 + }, 142 + inline .note_off, 143 + .note_on, 144 + .polyphonic_aftertouch, 145 + .control_change, 146 + .pitch_bend, 147 + => |data, tag| { 148 + const next_state_tag: std.meta.Tag(StreamState) = switch (tag) { 149 + else => unreachable, 150 + .note_off => .note_off_pitch, 151 + .note_on => .note_on_pitch, 152 + .polyphonic_aftertouch => .polyphonic_aftertouch_pitch, 153 + .control_change => .control_change_number, 154 + .pitch_bend => .pitch_bend_lsb, 155 + }; 156 + state.* = @unionInit( 157 + StreamState, 158 + @tagName(next_state_tag), 159 + .{ .channel = data.channel, .last_byte = payload.data }, 160 + ); 161 + }, 162 + inline .note_off_pitch, 163 + .note_on_pitch, 164 + .polyphonic_aftertouch_pitch, 165 + .control_change_number, 166 + .pitch_bend_lsb, 167 + => |data, tag| { 168 + const next_state_tag: std.meta.Tag(StreamState) = switch (tag) { 169 + else => unreachable, 170 + .note_off_pitch => .note_off, 171 + .note_on_pitch => .note_on, 172 + .polyphonic_aftertouch_pitch => .polyphonic_aftertouch, 173 + .control_change_number => .control_change, 174 + .pitch_bend_lsb => .pitch_bend, 175 + }; 176 + state.* = @unionInit( 177 + StreamState, 178 + @tagName(next_state_tag), 179 + .{ .channel = data.channel }, 180 + ); 181 + 182 + const message_tag = @tagName(next_state_tag); 183 + const status_payload = @unionInit( 184 + protocol.StatusPayload, 185 + message_tag, 186 + .init(data.last_byte, payload.data), 187 + ); 188 + 189 + out_queue.putOne( 190 + io, 191 + .{ .status = .{ .channel = data.channel, .payload = status_payload } }, 192 + ) catch |err| switch (err) { 193 + error.Canceled => return .{ .@"return" = error.Canceled }, 194 + error.Closed => return .@"break", 195 + }; 196 + }, 197 + inline .program_change, 198 + .channel_pressure, 199 + => |data, tag| { 200 + const message_tag = @tagName(tag); 201 + const status_payload = @unionInit( 202 + protocol.StatusPayload, 203 + message_tag, 204 + .init(payload.data), 205 + ); 206 + 207 + out_queue.putOne( 208 + io, 209 + .{ .status = .{ .channel = data.channel, .payload = status_payload } }, 210 + ) catch |err| switch (err) { 211 + error.Canceled => return .{ .@"return" = error.Canceled }, 212 + error.Closed => return .@"break", 213 + }; 214 + }, 215 + .mtc_quarter_frame => { 216 + state.* = .idle; 217 + const packed_frame = payload.mtc_quarter_frame; 218 + const unpacked_frame: protocol.MtcQuarterFrame = switch (packed_frame.piece_type) { 219 + inline .frame_lsb, 220 + .frame_msb, 221 + .second_lsb, 222 + .second_msb, 223 + .minute_lsb, 224 + .minute_msb, 225 + .hour_lsb, 226 + => |piece_type| @unionInit( 227 + protocol.MtcQuarterFrame, 228 + @tagName(piece_type), 229 + packed_frame.data.value, 230 + ), 231 + .hour_msb_framerate => .{ .hour_msb_framerate = .{ 232 + .hour_msb = packed_frame.data.hour_msb_framerate.hour_msb, 233 + .framerate = packed_frame.data.hour_msb_framerate.framerate, 234 + } }, 235 + }; 236 + if (mtc_frame_progress.add(unpacked_frame)) |smpte_frame| { 237 + out_queue.putOne(io, .{ .system = .{ 238 + .smpte_frame = smpte_frame, 239 + } }) catch |err| switch (err) { 240 + error.Canceled => return .{ .@"return" = error.Canceled }, 241 + error.Closed => return .@"break", 242 + }; 243 + } 244 + }, 245 + .song_position_pointer => { 246 + state.* = .{ .song_position_pointer_lsb = .{ .lsb = payload.data } }; 247 + }, 248 + .song_position_pointer_lsb => |data| { 249 + state.* = .idle; 250 + out_queue.putOne(io, .{ 251 + .system = .{ 252 + .song_position_pointer = .init(data.lsb, payload.data), 253 + }, 254 + }) catch |err| switch (err) { 255 + error.Canceled => return .{ .@"return" = error.Canceled }, 256 + error.Closed => return .@"break", 257 + }; 258 + }, 259 + .song_select => { 260 + state.* = .idle; 261 + out_queue.putOne(io, .{ .system = .{ 262 + .song_select = .{ .song_number = payload.data }, 263 + } }) catch |err| switch (err) { 264 + error.Canceled => return .{ .@"return" = error.Canceled }, 265 + error.Closed => return .@"break", 266 + }; 267 + }, 268 + } 269 + 270 + return .@"continue"; 271 + } 272 + 273 + const StreamState = union(enum) { 274 + idle, 275 + note_off: ChannelPayload, 276 + note_off_pitch: ChannelAndLastBytePayload, 277 + note_on: ChannelPayload, 278 + note_on_pitch: ChannelAndLastBytePayload, 279 + polyphonic_aftertouch: ChannelPayload, 280 + polyphonic_aftertouch_pitch: ChannelAndLastBytePayload, 281 + control_change: ChannelPayload, 282 + control_change_number: ChannelAndLastBytePayload, 283 + program_change: ChannelPayload, 284 + channel_pressure: ChannelPayload, 285 + pitch_bend: ChannelPayload, 286 + pitch_bend_lsb: ChannelAndLastBytePayload, 287 + system_exclusive: std.ArrayList(u7), 288 + // system 289 + mtc_quarter_frame, 290 + song_position_pointer, 291 + song_position_pointer_lsb: struct { lsb: u7 }, 292 + song_select, 293 + 294 + const ChannelPayload = struct { channel: u4 }; 295 + const ChannelAndLastBytePayload = struct { channel: u4, last_byte: u7 }; 296 + 297 + fn replace(self: *@This(), alloc: Allocator, new: @This()) void { 298 + self.deinit(alloc); 299 + self.* = new; 300 + } 301 + 302 + fn deinit(self: *@This(), alloc: Allocator) void { 303 + switch (self.*) { 304 + .system_exclusive => |*s| s.deinit(alloc), 305 + else => {}, 306 + } 307 + } 308 + }; 309 + 310 + const MtcFrameProgress = union(enum) { 311 + none, 312 + 313 + // direct order 314 + frame_lsb: u4, 315 + frame: protocol.SmpteFrame.Frame, 316 + frame_second_lsb: struct { 317 + frame: protocol.SmpteFrame.Frame, 318 + second_lsb: u4, 319 + }, 320 + frame_second: struct { 321 + frame: protocol.SmpteFrame.Frame, 322 + second: protocol.SmpteFrame.Second, 323 + }, 324 + frame_second_minute_lsb: struct { 325 + frame: protocol.SmpteFrame.Frame, 326 + second: protocol.SmpteFrame.Second, 327 + minute_lsb: u4, 328 + }, 329 + frame_second_minute: struct { 330 + frame: protocol.SmpteFrame.Frame, 331 + second: protocol.SmpteFrame.Second, 332 + minute: protocol.SmpteFrame.Minute, 333 + }, 334 + frame_second_minute_hour_lsb: struct { 335 + frame: protocol.SmpteFrame.Frame, 336 + second: protocol.SmpteFrame.Second, 337 + minute: protocol.SmpteFrame.Minute, 338 + hour_lsb: u4, 339 + }, 340 + 341 + // reverse order 342 + hour_msb_framerate: protocol.MtcQuarterFrame.HourMsbAndFramerate, 343 + hour_framerate: struct { 344 + hour: protocol.SmpteFrame.Hour, 345 + framerate: protocol.Framerate, 346 + }, 347 + minute_msb_hour_framerate: struct { 348 + minute_msb: u4, 349 + hour: protocol.SmpteFrame.Hour, 350 + framerate: protocol.Framerate, 351 + }, 352 + minute_hour_framerate: struct { 353 + minute: protocol.SmpteFrame.Minute, 354 + hour: protocol.SmpteFrame.Hour, 355 + framerate: protocol.Framerate, 356 + }, 357 + second_msb_minute_hour_framerate: struct { 358 + second_msb: u4, 359 + minute: protocol.SmpteFrame.Minute, 360 + hour: protocol.SmpteFrame.Hour, 361 + framerate: protocol.Framerate, 362 + }, 363 + second_minute_hour_framerate: struct { 364 + second: protocol.SmpteFrame.Second, 365 + minute: protocol.SmpteFrame.Minute, 366 + hour: protocol.SmpteFrame.Hour, 367 + framerate: protocol.Framerate, 368 + }, 369 + frame_msb_second_minute_hour_framerate: struct { 370 + frame_msb: u4, 371 + second: protocol.SmpteFrame.Second, 372 + minute: protocol.SmpteFrame.Minute, 373 + hour: protocol.SmpteFrame.Hour, 374 + framerate: protocol.Framerate, 375 + }, 376 + 377 + fn add(self: *@This(), value: protocol.MtcQuarterFrame) ?protocol.SmpteFrame { 378 + switch (value) { 379 + .frame_lsb => |lsb| switch (self.*) { 380 + .none => self.* = .{ .frame_lsb = lsb }, 381 + .frame_msb_second_minute_hour_framerate => |data| { 382 + self.* = .none; 383 + return protocol.SmpteFrame{ 384 + .frame = (@as(protocol.SmpteFrame.Frame, data.frame_msb) << 4) | lsb, 385 + .second = data.second, 386 + .minute = data.minute, 387 + .hour = data.hour, 388 + .framerate = data.framerate, 389 + }; 390 + }, 391 + else => self.* = .none, // reset the builder to avoid stream corruption 392 + }, 393 + .frame_msb => |msb| switch (self.*) { 394 + .frame_lsb => |lsb| self.* = .{ .frame = (@as(protocol.SmpteFrame.Frame, msb) << 4) | lsb }, 395 + .second_minute_hour_framerate => |data| self.* = .{ .frame_msb_second_minute_hour_framerate = .{ 396 + .frame_msb = msb, 397 + .second = data.second, 398 + .minute = data.minute, 399 + .hour = data.hour, 400 + .framerate = data.framerate, 401 + } }, 402 + else => self.* = .none, // reset the builder to avoid stream corruption 403 + }, 404 + .second_lsb => |lsb| switch (self.*) { 405 + .frame => |frame| self.* = .{ .frame_second_lsb = .{ 406 + .frame = frame, 407 + .second_lsb = lsb, 408 + } }, 409 + .second_msb_minute_hour_framerate => |data| self.* = .{ .second_minute_hour_framerate = .{ 410 + .second = (@as(protocol.SmpteFrame.Second, data.second_msb) << 4) | lsb, 411 + .minute = data.minute, 412 + .hour = data.hour, 413 + .framerate = data.framerate, 414 + } }, 415 + else => self.* = .none, // reset the builder to avoid stream corruption 416 + }, 417 + .second_msb => |msb| switch (self.*) { 418 + .frame_second_lsb => |data| self.* = .{ .frame_second = .{ 419 + .frame = data.frame, 420 + .second = (@as(protocol.SmpteFrame.Second, msb) << 4) | data.second_lsb, 421 + } }, 422 + .minute_hour_framerate => |data| self.* = .{ .second_msb_minute_hour_framerate = .{ 423 + .second_msb = msb, 424 + .minute = data.minute, 425 + .hour = data.hour, 426 + .framerate = data.framerate, 427 + } }, 428 + else => self.* = .none, // reset the builder to avoid stream corruption 429 + }, 430 + .minute_lsb => |lsb| switch (self.*) { 431 + .frame_second => |data| self.* = .{ .frame_second_minute_lsb = .{ 432 + .frame = data.frame, 433 + .second = data.second, 434 + .minute_lsb = lsb, 435 + } }, 436 + .minute_msb_hour_framerate => |data| self.* = .{ .minute_hour_framerate = .{ 437 + .minute = (@as(protocol.SmpteFrame.Minute, data.minute_msb) << 4) | lsb, 438 + .hour = data.hour, 439 + .framerate = data.framerate, 440 + } }, 441 + else => self.* = .none, // reset the builder to avoid stream corruption 442 + }, 443 + .minute_msb => |msb| switch (self.*) { 444 + .frame_second_minute_lsb => |data| self.* = .{ .frame_second_minute = .{ 445 + .frame = data.frame, 446 + .second = data.second, 447 + .minute = (@as(protocol.SmpteFrame.Minute, msb) << 4) | data.minute_lsb, 448 + } }, 449 + .hour_framerate => |data| self.* = .{ .minute_msb_hour_framerate = .{ 450 + .minute_msb = msb, 451 + .hour = data.hour, 452 + .framerate = data.framerate, 453 + } }, 454 + else => self.* = .none, // reset the builder to avoid stream corruption 455 + }, 456 + .hour_lsb => |lsb| switch (self.*) { 457 + .frame_second_minute => |data| self.* = .{ .frame_second_minute_hour_lsb = .{ 458 + .frame = data.frame, 459 + .second = data.second, 460 + .minute = data.minute, 461 + .hour_lsb = lsb, 462 + } }, 463 + .hour_msb_framerate => |data| self.* = .{ .hour_framerate = .{ 464 + .hour = (@as(protocol.SmpteFrame.Hour, data.hour_msb) << 4) | lsb, 465 + .framerate = data.framerate, 466 + } }, 467 + else => self.* = .none, // reset the builder to avoid stream corruption 468 + }, 469 + .hour_msb_framerate => |hour_msb_framerate| switch (self.*) { 470 + .frame_second_minute_hour_lsb => |data| { 471 + self.* = .none; 472 + return protocol.SmpteFrame{ 473 + .hour = (@as(protocol.SmpteFrame.Hour, hour_msb_framerate.hour_msb) << 4) | data.hour_lsb, 474 + .minute = data.minute, 475 + .second = data.second, 476 + .frame = data.frame, 477 + .framerate = hour_msb_framerate.framerate, 478 + }; 479 + }, 480 + .none => self.* = .{ .hour_msb_framerate = hour_msb_framerate }, 481 + else => self.* = .none, // reset the builder to avoid stream corruption 482 + }, 483 + } 484 + return null; 485 + } 486 + };
src/protocol.zig src/midi/protocol.zig
src/protocol/packed.zig src/midi/protocol/packed.zig
src/protocol/unpacked.zig src/midi/protocol/unpacked.zig
+1 -486
src/root.zig
··· 1 - const std = @import("std"); 2 - const Io = std.Io; 3 - const Allocator = std.mem.Allocator; 4 - 5 - pub const protocol = struct { 6 - const unpacked = @import("protocol/unpacked.zig"); 7 - const @"packed" = @import("protocol/packed.zig"); 8 - 9 - pub const MidiMessage = unpacked.MidiMessage; 10 - pub const Status = unpacked.Status; 11 - pub const StatusPayload = unpacked.StatusPayload; 12 - pub const SystemMessage = unpacked.SystemMessage; 13 - pub const SmpteFrame = unpacked.SmpteFrame; 14 - pub const MtcQuarterFrame = unpacked.MtcQuarterFrame; 15 - pub const Framerate = @import("protocol.zig").Framerate; 16 - pub const MtcPieceType = @import("protocol.zig").MtcPieceType; 17 - }; 18 - 19 - pub fn streamMidi( 20 - io: Io, 21 - gpa: Allocator, 22 - midi_dev_reader: *Io.Reader, 23 - out_queue: *Io.Queue(protocol.MidiMessage), 24 - ) (Allocator.Error || Io.Reader.Error || Io.Cancelable)!void { 25 - const MidiByte = protocol.@"packed".MidiByte; 26 - 27 - var state: StreamState = .idle; 28 - defer state.deinit(gpa); 29 - 30 - var mtc_frame_progress: MtcFrameProgress = .none; 31 - 32 - while (true) { 33 - const byte: MidiByte = @bitCast(try midi_dev_reader.takeByte()); 34 - switch (byte.kind) { 35 - .status => switch (handleStatusByte(io, gpa, &state, byte.payload.status, out_queue)) { 36 - .@"continue" => continue, 37 - .@"break" => break, 38 - .@"return" => |res| return res, 39 - }, 40 - .data => switch (handleDataByte(io, gpa, &state, &mtc_frame_progress, @bitCast(byte.payload), out_queue)) { 41 - .@"continue" => continue, 42 - .@"break" => break, 43 - .@"return" => |res| return res, 44 - }, 45 - } 46 - } 47 - } 48 - 49 - fn ControlFlow(comptime C: type, comptime B: type, comptime R: type) type { 50 - return union(enum) { 51 - @"continue": C, 52 - @"break": B, 53 - @"return": R, 54 - }; 55 - } 56 - 57 - inline fn handleStatusByte( 58 - io: Io, 59 - gpa: Allocator, 60 - state: *StreamState, 61 - status: protocol.@"packed".StatusByte, 62 - out_queue: *Io.Queue(protocol.MidiMessage), 63 - ) ControlFlow(void, void, (Io.Cancelable || Allocator.Error)!void) { 64 - switch (status.message) { 65 - .system_message => switch (status.payload.system) { 66 - .system_exclusive => state.replace(gpa, .{ .system_exclusive = .empty }), 67 - inline .mtc_quarter_frame, 68 - .song_position_pointer, 69 - .song_select, 70 - => |tag| state.replace(gpa, @unionInit(StreamState, @tagName(tag), {})), 71 - .end_of_exclusive => { 72 - const message = switch (state.*) { 73 - .system_exclusive => |*array_list| array_list.toOwnedSlice(gpa) catch |err| return .{ .@"return" = err }, 74 - else => { // missed the start of the message, so discard 75 - // std.debug.print("end of exclusive:\n", .{}); 76 - return .@"continue"; 77 - }, 78 - }; 79 - errdefer gpa.free(message); 80 - 81 - state.* = .idle; 82 - out_queue.putOne( 83 - io, 84 - .{ .system = .{ .system_exclusive = message } }, 85 - ) catch |err| switch (err) { 86 - error.Canceled => return .{ .@"return" = error.Canceled }, 87 - error.Closed => return .@"break", 88 - }; 89 - }, 90 - 91 - inline .tune_request, 92 - // real-time 93 - .timing_clock, 94 - .start, 95 - .@"continue", 96 - .stop, 97 - .active_sensing, 98 - .system_reset, 99 - => |tag| out_queue.putOne(io, .{ .system = @unionInit( 100 - protocol.SystemMessage, 101 - @tagName(tag), 102 - {}, 103 - ) }) catch |err| switch (err) { 104 - error.Canceled => return .{ .@"return" = error.Canceled }, 105 - error.Closed => return .@"break", 106 - }, 107 - }, 108 - inline .note_off, 109 - .note_on, 110 - .polyphonic_aftertouch, 111 - .control_change, 112 - .program_change, 113 - .channel_pressure, 114 - .pitch_bend, 115 - => |tag| state.replace(gpa, @unionInit( 116 - StreamState, 117 - @tagName(tag), 118 - .{ .channel = status.payload.channel }, 119 - )), 120 - } 121 - return .@"continue"; 122 - } 123 - 124 - inline fn handleDataByte( 125 - io: Io, 126 - gpa: Allocator, 127 - state: *StreamState, 128 - mtc_frame_progress: *MtcFrameProgress, 129 - payload: packed union(u7) { 130 - data: u7, 131 - mtc_quarter_frame: protocol.@"packed".MtcQuarterFrame, 132 - }, 133 - out_queue: *Io.Queue(protocol.MidiMessage), 134 - ) ControlFlow(void, void, (Io.Cancelable || Allocator.Error)!void) { 135 - switch (state.*) { 136 - .idle => { // missed the start of the message, so no clue where to put the data 137 - // std.debug.print("data byte when idle: {x}\n", .{byte.payload.data}); 138 - }, 139 - .system_exclusive => |*message| { 140 - message.append(gpa, payload.data) catch |err| return .{ .@"return" = err }; 141 - }, 142 - inline .note_off, 143 - .note_on, 144 - .polyphonic_aftertouch, 145 - .control_change, 146 - .pitch_bend, 147 - => |data, tag| { 148 - const next_state_tag: std.meta.Tag(StreamState) = switch (tag) { 149 - else => unreachable, 150 - .note_off => .note_off_pitch, 151 - .note_on => .note_on_pitch, 152 - .polyphonic_aftertouch => .polyphonic_aftertouch_pitch, 153 - .control_change => .control_change_number, 154 - .pitch_bend => .pitch_bend_lsb, 155 - }; 156 - state.* = @unionInit( 157 - StreamState, 158 - @tagName(next_state_tag), 159 - .{ .channel = data.channel, .last_byte = payload.data }, 160 - ); 161 - }, 162 - inline .note_off_pitch, 163 - .note_on_pitch, 164 - .polyphonic_aftertouch_pitch, 165 - .control_change_number, 166 - .pitch_bend_lsb, 167 - => |data, tag| { 168 - const next_state_tag: std.meta.Tag(StreamState) = switch (tag) { 169 - else => unreachable, 170 - .note_off_pitch => .note_off, 171 - .note_on_pitch => .note_on, 172 - .polyphonic_aftertouch_pitch => .polyphonic_aftertouch, 173 - .control_change_number => .control_change, 174 - .pitch_bend_lsb => .pitch_bend, 175 - }; 176 - state.* = @unionInit( 177 - StreamState, 178 - @tagName(next_state_tag), 179 - .{ .channel = data.channel }, 180 - ); 181 - 182 - const message_tag = @tagName(next_state_tag); 183 - const status_payload = @unionInit( 184 - protocol.StatusPayload, 185 - message_tag, 186 - .init(data.last_byte, payload.data), 187 - ); 188 - 189 - out_queue.putOne( 190 - io, 191 - .{ .status = .{ .channel = data.channel, .payload = status_payload } }, 192 - ) catch |err| switch (err) { 193 - error.Canceled => return .{ .@"return" = error.Canceled }, 194 - error.Closed => return .@"break", 195 - }; 196 - }, 197 - inline .program_change, 198 - .channel_pressure, 199 - => |data, tag| { 200 - const message_tag = @tagName(tag); 201 - const status_payload = @unionInit( 202 - protocol.StatusPayload, 203 - message_tag, 204 - .init(payload.data), 205 - ); 206 - 207 - out_queue.putOne( 208 - io, 209 - .{ .status = .{ .channel = data.channel, .payload = status_payload } }, 210 - ) catch |err| switch (err) { 211 - error.Canceled => return .{ .@"return" = error.Canceled }, 212 - error.Closed => return .@"break", 213 - }; 214 - }, 215 - .mtc_quarter_frame => { 216 - state.* = .idle; 217 - const packed_frame = payload.mtc_quarter_frame; 218 - const unpacked_frame: protocol.MtcQuarterFrame = switch (packed_frame.piece_type) { 219 - inline .frame_lsb, 220 - .frame_msb, 221 - .second_lsb, 222 - .second_msb, 223 - .minute_lsb, 224 - .minute_msb, 225 - .hour_lsb, 226 - => |piece_type| @unionInit( 227 - protocol.MtcQuarterFrame, 228 - @tagName(piece_type), 229 - packed_frame.data.value, 230 - ), 231 - .hour_msb_framerate => .{ .hour_msb_framerate = .{ 232 - .hour_msb = packed_frame.data.hour_msb_framerate.hour_msb, 233 - .framerate = packed_frame.data.hour_msb_framerate.framerate, 234 - } }, 235 - }; 236 - if (mtc_frame_progress.add(unpacked_frame)) |smpte_frame| { 237 - out_queue.putOne(io, .{ .system = .{ 238 - .smpte_frame = smpte_frame, 239 - } }) catch |err| switch (err) { 240 - error.Canceled => return .{ .@"return" = error.Canceled }, 241 - error.Closed => return .@"break", 242 - }; 243 - } 244 - }, 245 - .song_position_pointer => { 246 - state.* = .{ .song_position_pointer_lsb = .{ .lsb = payload.data } }; 247 - }, 248 - .song_position_pointer_lsb => |data| { 249 - state.* = .idle; 250 - out_queue.putOne(io, .{ 251 - .system = .{ 252 - .song_position_pointer = .init(data.lsb, payload.data), 253 - }, 254 - }) catch |err| switch (err) { 255 - error.Canceled => return .{ .@"return" = error.Canceled }, 256 - error.Closed => return .@"break", 257 - }; 258 - }, 259 - .song_select => { 260 - state.* = .idle; 261 - out_queue.putOne(io, .{ .system = .{ 262 - .song_select = .{ .song_number = payload.data }, 263 - } }) catch |err| switch (err) { 264 - error.Canceled => return .{ .@"return" = error.Canceled }, 265 - error.Closed => return .@"break", 266 - }; 267 - }, 268 - } 269 - 270 - return .@"continue"; 271 - } 272 - 273 - const StreamState = union(enum) { 274 - idle, 275 - note_off: ChannelPayload, 276 - note_off_pitch: ChannelAndLastBytePayload, 277 - note_on: ChannelPayload, 278 - note_on_pitch: ChannelAndLastBytePayload, 279 - polyphonic_aftertouch: ChannelPayload, 280 - polyphonic_aftertouch_pitch: ChannelAndLastBytePayload, 281 - control_change: ChannelPayload, 282 - control_change_number: ChannelAndLastBytePayload, 283 - program_change: ChannelPayload, 284 - channel_pressure: ChannelPayload, 285 - pitch_bend: ChannelPayload, 286 - pitch_bend_lsb: ChannelAndLastBytePayload, 287 - system_exclusive: std.ArrayList(u7), 288 - // system 289 - mtc_quarter_frame, 290 - song_position_pointer, 291 - song_position_pointer_lsb: struct { lsb: u7 }, 292 - song_select, 293 - 294 - const ChannelPayload = struct { channel: u4 }; 295 - const ChannelAndLastBytePayload = struct { channel: u4, last_byte: u7 }; 296 - 297 - fn replace(self: *@This(), alloc: Allocator, new: @This()) void { 298 - self.deinit(alloc); 299 - self.* = new; 300 - } 301 - 302 - fn deinit(self: *@This(), alloc: Allocator) void { 303 - switch (self.*) { 304 - .system_exclusive => |*s| s.deinit(alloc), 305 - else => {}, 306 - } 307 - } 308 - }; 309 - 310 - const MtcFrameProgress = union(enum) { 311 - none, 312 - 313 - // direct order 314 - frame_lsb: u4, 315 - frame: protocol.SmpteFrame.Frame, 316 - frame_second_lsb: struct { 317 - frame: protocol.SmpteFrame.Frame, 318 - second_lsb: u4, 319 - }, 320 - frame_second: struct { 321 - frame: protocol.SmpteFrame.Frame, 322 - second: protocol.SmpteFrame.Second, 323 - }, 324 - frame_second_minute_lsb: struct { 325 - frame: protocol.SmpteFrame.Frame, 326 - second: protocol.SmpteFrame.Second, 327 - minute_lsb: u4, 328 - }, 329 - frame_second_minute: struct { 330 - frame: protocol.SmpteFrame.Frame, 331 - second: protocol.SmpteFrame.Second, 332 - minute: protocol.SmpteFrame.Minute, 333 - }, 334 - frame_second_minute_hour_lsb: struct { 335 - frame: protocol.SmpteFrame.Frame, 336 - second: protocol.SmpteFrame.Second, 337 - minute: protocol.SmpteFrame.Minute, 338 - hour_lsb: u4, 339 - }, 340 - 341 - // reverse order 342 - hour_msb_framerate: protocol.MtcQuarterFrame.HourMsbAndFramerate, 343 - hour_framerate: struct { 344 - hour: protocol.SmpteFrame.Hour, 345 - framerate: protocol.Framerate, 346 - }, 347 - minute_msb_hour_framerate: struct { 348 - minute_msb: u4, 349 - hour: protocol.SmpteFrame.Hour, 350 - framerate: protocol.Framerate, 351 - }, 352 - minute_hour_framerate: struct { 353 - minute: protocol.SmpteFrame.Minute, 354 - hour: protocol.SmpteFrame.Hour, 355 - framerate: protocol.Framerate, 356 - }, 357 - second_msb_minute_hour_framerate: struct { 358 - second_msb: u4, 359 - minute: protocol.SmpteFrame.Minute, 360 - hour: protocol.SmpteFrame.Hour, 361 - framerate: protocol.Framerate, 362 - }, 363 - second_minute_hour_framerate: struct { 364 - second: protocol.SmpteFrame.Second, 365 - minute: protocol.SmpteFrame.Minute, 366 - hour: protocol.SmpteFrame.Hour, 367 - framerate: protocol.Framerate, 368 - }, 369 - frame_msb_second_minute_hour_framerate: struct { 370 - frame_msb: u4, 371 - second: protocol.SmpteFrame.Second, 372 - minute: protocol.SmpteFrame.Minute, 373 - hour: protocol.SmpteFrame.Hour, 374 - framerate: protocol.Framerate, 375 - }, 376 - 377 - fn add(self: *@This(), value: protocol.MtcQuarterFrame) ?protocol.SmpteFrame { 378 - switch (value) { 379 - .frame_lsb => |lsb| switch (self.*) { 380 - .none => self.* = .{ .frame_lsb = lsb }, 381 - .frame_msb_second_minute_hour_framerate => |data| { 382 - self.* = .none; 383 - return protocol.SmpteFrame{ 384 - .frame = (@as(protocol.SmpteFrame.Frame, data.frame_msb) << 4) | lsb, 385 - .second = data.second, 386 - .minute = data.minute, 387 - .hour = data.hour, 388 - .framerate = data.framerate, 389 - }; 390 - }, 391 - else => self.* = .none, // reset the builder to avoid stream corruption 392 - }, 393 - .frame_msb => |msb| switch (self.*) { 394 - .frame_lsb => |lsb| self.* = .{ .frame = (@as(protocol.SmpteFrame.Frame, msb) << 4) | lsb }, 395 - .second_minute_hour_framerate => |data| self.* = .{ .frame_msb_second_minute_hour_framerate = .{ 396 - .frame_msb = msb, 397 - .second = data.second, 398 - .minute = data.minute, 399 - .hour = data.hour, 400 - .framerate = data.framerate, 401 - } }, 402 - else => self.* = .none, // reset the builder to avoid stream corruption 403 - }, 404 - .second_lsb => |lsb| switch (self.*) { 405 - .frame => |frame| self.* = .{ .frame_second_lsb = .{ 406 - .frame = frame, 407 - .second_lsb = lsb, 408 - } }, 409 - .second_msb_minute_hour_framerate => |data| self.* = .{ .second_minute_hour_framerate = .{ 410 - .second = (@as(protocol.SmpteFrame.Second, data.second_msb) << 4) | lsb, 411 - .minute = data.minute, 412 - .hour = data.hour, 413 - .framerate = data.framerate, 414 - } }, 415 - else => self.* = .none, // reset the builder to avoid stream corruption 416 - }, 417 - .second_msb => |msb| switch (self.*) { 418 - .frame_second_lsb => |data| self.* = .{ .frame_second = .{ 419 - .frame = data.frame, 420 - .second = (@as(protocol.SmpteFrame.Second, msb) << 4) | data.second_lsb, 421 - } }, 422 - .minute_hour_framerate => |data| self.* = .{ .second_msb_minute_hour_framerate = .{ 423 - .second_msb = msb, 424 - .minute = data.minute, 425 - .hour = data.hour, 426 - .framerate = data.framerate, 427 - } }, 428 - else => self.* = .none, // reset the builder to avoid stream corruption 429 - }, 430 - .minute_lsb => |lsb| switch (self.*) { 431 - .frame_second => |data| self.* = .{ .frame_second_minute_lsb = .{ 432 - .frame = data.frame, 433 - .second = data.second, 434 - .minute_lsb = lsb, 435 - } }, 436 - .minute_msb_hour_framerate => |data| self.* = .{ .minute_hour_framerate = .{ 437 - .minute = (@as(protocol.SmpteFrame.Minute, data.minute_msb) << 4) | lsb, 438 - .hour = data.hour, 439 - .framerate = data.framerate, 440 - } }, 441 - else => self.* = .none, // reset the builder to avoid stream corruption 442 - }, 443 - .minute_msb => |msb| switch (self.*) { 444 - .frame_second_minute_lsb => |data| self.* = .{ .frame_second_minute = .{ 445 - .frame = data.frame, 446 - .second = data.second, 447 - .minute = (@as(protocol.SmpteFrame.Minute, msb) << 4) | data.minute_lsb, 448 - } }, 449 - .hour_framerate => |data| self.* = .{ .minute_msb_hour_framerate = .{ 450 - .minute_msb = msb, 451 - .hour = data.hour, 452 - .framerate = data.framerate, 453 - } }, 454 - else => self.* = .none, // reset the builder to avoid stream corruption 455 - }, 456 - .hour_lsb => |lsb| switch (self.*) { 457 - .frame_second_minute => |data| self.* = .{ .frame_second_minute_hour_lsb = .{ 458 - .frame = data.frame, 459 - .second = data.second, 460 - .minute = data.minute, 461 - .hour_lsb = lsb, 462 - } }, 463 - .hour_msb_framerate => |data| self.* = .{ .hour_framerate = .{ 464 - .hour = (@as(protocol.SmpteFrame.Hour, data.hour_msb) << 4) | lsb, 465 - .framerate = data.framerate, 466 - } }, 467 - else => self.* = .none, // reset the builder to avoid stream corruption 468 - }, 469 - .hour_msb_framerate => |hour_msb_framerate| switch (self.*) { 470 - .frame_second_minute_hour_lsb => |data| { 471 - self.* = .none; 472 - return protocol.SmpteFrame{ 473 - .hour = (@as(protocol.SmpteFrame.Hour, hour_msb_framerate.hour_msb) << 4) | data.hour_lsb, 474 - .minute = data.minute, 475 - .second = data.second, 476 - .frame = data.frame, 477 - .framerate = hour_msb_framerate.framerate, 478 - }; 479 - }, 480 - .none => self.* = .{ .hour_msb_framerate = hour_msb_framerate }, 481 - else => self.* = .none, // reset the builder to avoid stream corruption 482 - }, 483 - } 484 - return null; 485 - } 486 - }; 1 + pub const midi = @import("midi.zig");

History

3 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
feat!: Move MIDI-related code into the midi module; Rename root module to midi-synth
merge conflicts detected
expand
  • build.zig:28
  • src/main.zig:2
  • src/root.zig:1
expand 0 comments
1 commit
expand
feat!: Move MIDI-related code into the midi module; Rename root module to midi-synth
expand 0 comments
1 commit
expand
feat!: Move MIDI-related code into the midi module; Rename root module to midi-synth
expand 0 comments