| @@ -0,0 +1,171 @@ |
| | 1 | // Scream-IVSHMEM receiver for ALSA. |
| | 2 | // Author: Marco Martinelli - https://github.com/martinellimarco |
| | 3 | |
| | 4 | #include <stdio.h> |
| | 5 | #include <stdlib.h> |
| | 6 | #include <string.h> |
| | 7 | #include <stdint.h> |
| | 8 | #include <unistd.h> |
| | 9 | #include <fcntl.h> |
| | 10 | #include <sys/ioctl.h> |
| | 11 | #include <sys/stat.h> |
| | 12 | #include <sys/mman.h> |
| | 13 | |
| | 14 | struct shmheader { |
| | 15 | uint32_t magic; |
| | 16 | uint16_t write_idx; |
| | 17 | uint8_t offset; |
| | 18 | uint16_t max_chunks; |
| | 19 | uint32_t chunk_size; |
| | 20 | uint8_t sample_rate; |
| | 21 | uint8_t sample_size; |
| | 22 | uint8_t channels; |
| | 23 | uint16_t channel_map; |
| | 24 | }; |
| | 25 | |
| | 26 | static void show_usage(const char *arg0) |
| | 27 | { |
| | 28 | fprintf(stderr, "\n"); |
| | 29 | fprintf(stderr, "Usage: %s <ivshmem device path> [-v]\n", arg0); |
| | 30 | fprintf(stderr, "\n"); |
| | 31 | fprintf(stderr, " -v : Verbose operation.\n"); |
| | 32 | fprintf(stderr, "\n"); |
| | 33 | exit(1); |
| | 34 | } |
| | 35 | |
| | 36 | static void * open_mmap(const char *shmfile) { |
| | 37 | struct stat st; |
| | 38 | if (stat(shmfile, &st) < 0) { |
| | 39 | fprintf(stderr, "Failed to stat the shared memory file: %s", shmfile); |
| | 40 | exit(2); |
| | 41 | } |
| | 42 | |
| | 43 | int shmFD = open(shmfile, O_RDWR, (mode_t)0600); |
| | 44 | if (shmFD < 0) { |
| | 45 | fprintf(stderr, "Failed to open the shared memory file: %s", shmfile); |
| | 46 | exit(3); |
| | 47 | } |
| | 48 | |
| | 49 | void * map = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmFD, 0); |
| | 50 | if (map == MAP_FAILED) { |
| | 51 | fprintf(stderr, "Failed to map the shared memory file: %s", shmfile); |
| | 52 | close(shmFD); |
| | 53 | exit(4); |
| | 54 | } |
| | 55 | |
| | 56 | return map; |
| | 57 | } |
| | 58 | |
| | 59 | int main(int argc, char *argv[]) |
| | 60 | { |
| | 61 | if (argc < 2) { |
| | 62 | show_usage(argv[0]); |
| | 63 | } |
| | 64 | |
| | 65 | unsigned char * mmap = open_mmap(argv[1]); |
| | 66 | |
| | 67 | int opt; |
| | 68 | int verbosity = 0; |
| | 69 | int samples; |
| | 70 | |
| | 71 | unsigned int bytes_per_sample = 2; |
| | 72 | unsigned int rate = 44100; |
| | 73 | unsigned char cur_sample_rate = 0; |
| | 74 | unsigned char cur_sample_size = 0; |
| | 75 | unsigned char cur_channels = 2; |
| | 76 | uint16_t cur_channel_map = 0x0003; |
| | 77 | |
| | 78 | while ((opt = getopt(argc, argv, "v")) != -1) { |
| | 79 | switch (opt) { |
| | 80 | case 'v': |
| | 81 | verbosity += 1; |
| | 82 | break; |
| | 83 | default: |
| | 84 | show_usage(argv[0]); |
| | 85 | } |
| | 86 | } |
| | 87 | if (1+optind < argc) { |
| | 88 | fprintf(stderr, "Expected argument after options\n"); |
| | 89 | show_usage(argv[0]); |
| | 90 | } |
| | 91 | |
| | 92 | struct shmheader *header = (struct shmheader*)mmap; |
| | 93 | uint16_t read_idx = header->write_idx; |
| | 94 | |
| | 95 | for (;;) { |
| | 96 | if (header->magic != 0x11112014) { |
| | 97 | while (header->magic != 0x11112014) { |
| | 98 | usleep(10000);//10ms |
| | 99 | } |
| | 100 | read_idx = header->write_idx; |
| | 101 | } |
| | 102 | if (read_idx == header->write_idx) { |
| | 103 | usleep(10000);//10ms |
| | 104 | continue; |
| | 105 | } |
| | 106 | if (++read_idx == header->max_chunks) { |
| | 107 | read_idx = 0; |
| | 108 | } |
| | 109 | unsigned char *buf = &mmap[header->offset+header->chunk_size*read_idx]; |
| | 110 | |
| | 111 | // Change rate/size? |
| | 112 | if ( cur_sample_rate != header->sample_rate |
| | 113 | || cur_sample_size != header->sample_size |
| | 114 | || cur_channels != header->channels |
| | 115 | || cur_channel_map != header->channel_map) { |
| | 116 | |
| | 117 | cur_sample_rate = header->sample_rate; |
| | 118 | cur_sample_size = header->sample_size; |
| | 119 | cur_channels = header->channels; |
| | 120 | cur_channel_map = header->channel_map; |
| | 121 | |
| | 122 | rate = ((cur_sample_rate >= 128) ? 44100 : 48000) * (cur_sample_rate % 128); |
| | 123 | switch (cur_sample_size) { |
| | 124 | case 16: bytes_per_sample = 2; break; |
| | 125 | case 24: bytes_per_sample = 3; break; |
| | 126 | case 32: bytes_per_sample = 4; break; |
| | 127 | default: |
| | 128 | if (verbosity > 0) { |
| | 129 | fprintf(stderr, "Unsupported sample size %hhu, not playing until next format switch.\n", cur_sample_size); |
| | 130 | } |
| | 131 | rate = 0; |
| | 132 | } |
| | 133 | |
| | 134 | if (cur_channels > 2) { |
| | 135 | int k = -1; |
| | 136 | for (int i=0; i<cur_channels; i++) { |
| | 137 | for (int j = k+1; j<=10; j++) {// check the channel map bit by bit from lsb to msb, starting from were we left on the previous step |
| | 138 | if ((cur_channel_map >> j) & 0x01) {// if the bit in j position is set then we have the key for this channel |
| | 139 | k = j; |
| | 140 | break; |
| | 141 | } |
| | 142 | } |
| | 143 | if (verbosity > 0) { |
| | 144 | const char *channel_name; |
| | 145 | switch (k) { |
| | 146 | case 0: channel_name = "Front Left"; break; |
| | 147 | case 1: channel_name = "Front Right"; break; |
| | 148 | case 2: channel_name = "Front Center"; break; |
| | 149 | case 3: channel_name = "LFE / Subwoofer"; break; |
| | 150 | case 4: channel_name = "Rear Left"; break; |
| | 151 | case 5: channel_name = "Rear Right"; break; |
| | 152 | case 6: channel_name = "Front-Left Center"; break; |
| | 153 | case 7: channel_name = "Front-Right Center"; break; |
| | 154 | case 8: channel_name = "Rear Center"; break; |
| | 155 | case 9: channel_name = "Side Left"; break; |
| | 156 | case 10: channel_name = "Side Right"; break; |
| | 157 | default: |
| | 158 | channel_name = "Unknown. Setted to Center."; |
| | 159 | } |
| | 160 | fprintf(stderr, "Channel %i is %s\n", i, channel_name); |
| | 161 | } |
| | 162 | } |
| | 163 | } |
| | 164 | } |
| | 165 | if (!rate) continue; |
| | 166 | |
| | 167 | samples = (header->chunk_size) / (bytes_per_sample * cur_channels); |
| | 168 | fwrite(buf, bytes_per_sample*cur_channels, samples, stdout); |
| | 169 | |
| | 170 | } |
| | 171 | } |