View difference between Paste ID: v8gTWBtJ and 0PMyfPvK
SHOW: | | - or go back to the newest paste.
1
/*
2-
** gcc -o mix mix.c -lao -lsndfile -lm
2+
** gcc -o mix mix.c -lao -lsndfile -lm -lpthread
3
**
4
*/
5
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <stdint.h>
9
#include <string.h>
10
#include <math.h>
11
#include <ao/ao.h>
12
#include <sndfile.h>
13
#include <pthread.h>
14
15
#define BUFFSIZE 512
16-
int playfiles (int, FILE *[], int[]);
16+
17
typedef struct _sound {
18
    SNDFILE *file;
19
    SF_INFO *info;
20-
    int filesc = (argc - 1) / 2;
20+
    float gain;
21-
    FILE *fp[filesc];
21+
} Sound;
22-
    int starttime[filesc];
22+
23
typedef struct _play_item PlayItem;
24-
    if (argc < 2 || (argc - 1) % 2 != 0) {
24+
25-
        printf("usage: %s file1 startsec1 file2 startsec2 fileN startsecN...\n", argv[0]);
25+
struct _play_item {
26
    Sound *sound;
27
    PlayItem *next;
28-
    for(int i=0; i < filesc; i++) {
28+
};
29-
        fp[i] = fopen(argv[i * 2 + 1], "rb");
29+
30-
        starttime[i] = atoi(argv[i * 2 + 2]);
30+
Sound *new_sound (const char *filename, float gain);
31-
        if (fp[i] == NULL)  {
31+
void free_sound (Sound *sound);
32-
            printf("Cannot open %s.\n", argv[i+1]);
32+
void play_sound (Sound *sound);
33-
            exit(-1);
33+
34
static PlayItem *new_play_item ();
35
static void free_play_item (PlayItem *);
36
static void queue_play_item (PlayItem *);
37
38-
    playfiles (filesc, fp, starttime);
38+
static void *mix (void *arg);
39
40
static PlayItem *play_queue = NULL;
41
static volatile int mix_thread_exit_requested = 0;
42
static pthread_mutex_t mix_mutex = PTHREAD_MUTEX_INITIALIZER;
43
44-
int playfiles (int filesc, FILE *fp[], int starttime[])
44+
45
{
46
    pthread_t mix_thread;
47
    int sndc = (argc - 1);
48
    char *table[sndc];
49-
    SNDFILE *sndfile[filesc];
49+
50-
    SF_INFO sf_info[filesc];
50+
    if (argc < 2 || argc > 10) {
51-
    int startframe[filesc];
51+
        printf("usage: %s file1 file2 fileN ...\nN < 10\n", argv[0]);
52-
    float gain = 1.0f / filesc;
52+
53
    }
54
55
    printf ("\nadding files:\n");
56
    for(int i=0; i < sndc; i++) {
57
        table[i] = argv[i + 1];
58
        printf ("    %d - file %s\n", i + 1, table[i]);
59
    }
60-
    for(int i=0; i < filesc; i++) {
60+
61-
        sndfile[i] = sf_open_fd(fileno(fp[i]), SFM_READ, &sf_info[i], 1);
61+
62-
        toread += sf_info[i].frames * sf_info[i].channels;
62+
63-
        // convert starttime to startframe
63+
    // create the mix thread
64-
        startframe[i] = (int) (((float) (sf_info[0].samplerate * sf_info[0].channels) / (float) BUFFSIZE) * starttime[i]);
64+
    if(pthread_create(&mix_thread, NULL, &mix, NULL)) {
65-
        printf ("File %d start time %d sec, %d frame to skip\n", i, starttime[i], startframe[i]);
65+
        fprintf (stderr, "error creating mixing thread\n");
66
        exit (-2);
67
    }
68
69-
    buflen = BUFFSIZE * sf_info[0].channels;
69+
    printf ("mixing thread started\n\n");
70
    printf ("write a number between 1 and %d to play a file (Q or q to quit).\n", sndc);
71
72
    int c;   
73
    while((c=getchar())!= EOF) {
74
        if (c == 'Q' || c == 'q') {
75
            printf ("request mixing thread to finish\n");
76
            pthread_mutex_lock(&mix_mutex);
77
            mix_thread_exit_requested = 1;
78
            pthread_mutex_unlock(&mix_mutex);
79-
    format.channels = sf_info[0].channels;
79+
            break;
80-
    format.rate = sf_info[0].samplerate;
80+
        } else if (c > 48 && c < 57) {
81
            c = c - 49;
82
            if (c < sndc) {
83
                printf ("playing sound %s\n", table[c]);
84
                Sound *sound = new_sound (table[c], 1.0f / sndc);
85
                play_sound (sound);
86
            } else {
87
                printf ("no sound at slot %d\n", c + 1);
88-
    while (toread > 0) {
88+
89
        } else if ( c != '\n') {
90
            printf ("invalid char %c", c);
91
        }
92
    }
93-
        for(int i=0; i < filesc; i++) {
93+
94-
            if (startframe[i] == 0) {
94+
    printf ("waiting mixing thread to finish\n");
95-
                sf_count_t item_read = sf_read_float (sndfile[i], filebuffer, BUFFSIZE); // WHY BUFFSIZE? Shouldn't be BUFFSIZE * channels?
95+
96
    if(pthread_join(mix_thread, NULL)) {
97-
                if (item_read > 0) {
97+
        fprintf(stderr, "error joining mixing thread\n");
98-
                    toread -= item_read;
98+
        exit (-3);
99
    }
100-
                    // mix samples
100+
101-
                    for(int item = 0; item < item_read; item++) {
101+
    printf ("cleanup remaining play items\n");
102-
                        mixbuffer[item] += (filebuffer[item] * gain);
102+
    PlayItem *current = play_queue;
103-
                    }
103+
    while (current != NULL) {
104
        PlayItem *tmp = current;
105
        current = current->next;
106-
                startframe[i]--;
106+
107
        fprintf (stderr, "   * cleanup item %p\n",tmp);
108
        free_sound (tmp->sound);
109
        free_play_item (tmp);
110
    }
111
112
    ao_shutdown ();
113
114
    printf ("bye\n");
115
    return 0;
116
}
117
118
Sound* new_sound (const char *filename, float gain) {
119
    Sound *snd = malloc(sizeof(Sound));
120
121
    snd->gain = gain;
122
    snd->info = malloc(sizeof(SF_INFO));
123
    snd->info->format = 0;
124
    snd->file = sf_open (filename, SFM_READ, snd->info);
125
    if (snd->file == NULL) {
126
        fprintf (stderr, "error opening '%s': %s\n", filename, sf_strerror (NULL));
127-
    for(int i=0; i < filesc; i++) {
127+
128-
        sf_close(sndfile[i]);
128+
    return snd;
129
}
130
131
void free_sound (Sound *sound) {
132
    free (sound->info);
133
    sf_close (sound->file);
134
    free (sound);
135
}
136
137
void play_sound (Sound *sound) {
138
    PlayItem *item = new_play_item ();
139
    item->sound = sound;
140
141
    queue_play_item (item);
142
}
143
144
PlayItem *new_play_item () {
145
    PlayItem *item = malloc(sizeof(PlayItem));
146
    item->next = NULL;
147
148
    return item;
149
}
150
151
void free_play_item (PlayItem *item) {
152
    free (item);
153
}
154
155
void queue_play_item (PlayItem *item) {
156
    if (item == NULL)
157
        return;
158
159
    fprintf (stderr, "   * queue item %p\n", item);
160
    pthread_mutex_lock(&mix_mutex);
161
    if (play_queue == NULL)
162
        play_queue = item;
163
    else
164
        play_queue->next = item;
165
166
    pthread_mutex_unlock(&mix_mutex);
167
}
168
169
/* 
170
 * Mixing function
171
 *
172
 * Limits:
173
 *
174
 *   No RT safe: use IO functions and mutex
175
 *   Push model: better to use pull from the device driver (can I do this with libao?)
176
 *   All the sample must be in the same format (both sample rate and channels)
177
 *
178
 */
179
void *mix (void *arg)
180
{
181
    int default_driver;
182
    ao_device *device;
183
    ao_sample_format format;
184
    float *filebuffer;
185
    float *mixbuffer;
186
    short *outbuffer;
187
188
    int buflen;
189
    int toread = 0;
190
191
    // all files MUST be in the same format / channels
192
    buflen = BUFFSIZE * 2;
193
    filebuffer = malloc(buflen * sizeof(float));
194
    mixbuffer= malloc(buflen * sizeof(float));
195
    outbuffer= malloc(buflen * sizeof(short));
196
197
    default_driver = ao_default_driver_id();
198
    memset(&format, 0, sizeof(ao_sample_format));
199
200
    format.byte_format = AO_FMT_NATIVE;
201
    format.bits = 16;
202
    format.channels = 2;
203
    format.rate = 44100;
204
205
    device = ao_open_live(default_driver, &format, NULL);
206
    if (device == NULL) {
207
        printf("Error opening sound device.\n");
208
        exit(-2);
209
    }
210
211
    while (1) {
212
213
        pthread_mutex_lock(&mix_mutex);
214
        int running = mix_thread_exit_requested == 0;
215
        pthread_mutex_unlock(&mix_mutex);
216
217
        if (!running)
218
            break;
219
220
        for(int item = 0; item < buflen; item++) {
221
            mixbuffer[item] = 0;
222
        }
223
224
        pthread_mutex_lock(&mix_mutex);
225
226
        PlayItem *current = play_queue;
227
        PlayItem *previous = NULL;
228
        while (current != NULL) {
229
            sf_count_t item_read = sf_read_float (current->sound->file, filebuffer, BUFFSIZE); // WHY BUFFSIZE? Shouldn't be BUFFSIZE * channels?
230
231
            if (item_read > 0) {
232
                toread -= item_read;
233
234
                // mix samples
235
                for(int item = 0; item < item_read; item++) {
236
                    mixbuffer[item] += (filebuffer[item] * current->sound->gain);
237
                }
238
                previous = current;
239
                current = current->next;
240
            } else {
241
               // dequeue element
242
               PlayItem *tmp = current;
243
244
               current = current->next;
245
246
               if (tmp == play_queue)
247
                   play_queue = current;
248
               else
249
                   previous->next = current;
250
251
               fprintf (stderr, "   * dequeue item %p\n", tmp);
252
               free_sound (tmp->sound);
253
               free_play_item (tmp);
254
            }
255
        }
256
        pthread_mutex_unlock(&mix_mutex);
257
258
        // convert from float to short
259
        for(int item = 0; item < buflen; item++) {
260
            int value = mixbuffer[item] * 32768;
261
262
            // limit to valid short values
263
            if (value > 32767)
264
                value = 32767;
265
            else if (value < -32768)
266
                value = -32768;
267
268
            outbuffer[item] =  (short) value; 
269
        }
270
271
        ao_play(device, (char *)outbuffer, buflen);
272
    }
273
274
    ao_close(device);
275
    free(filebuffer);
276
    free(mixbuffer);
277
    free(outbuffer);
278
279
    return NULL;
280
}